<?php

namespace App\Http\Controllers;

use App\Models\IntegrationSetting;
use App\Models\IntegrationSyncLog;
use App\Models\Lead;
use App\Models\LeadStage;
use App\Models\Message;
use App\Models\Activity;
use App\Models\CampaignTarget;
use App\Models\Contact;
use App\Models\ContactEmail;
use App\Models\IntegrationFieldMapping;
use App\Models\IntegrationLink;
use App\Models\Ticket;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Database\QueryException;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;

class IntegrationWebhookController extends Controller
{
    public function receive(Request $request, string $provider): JsonResponse
    {
        abort_unless(in_array($provider, ['facebook', 'whatsapp', 'mailchimp', 'jira'], true), 404);
        $businessId = (int) $request->input('business_id');
        $setting = IntegrationSetting::where('business_id', $businessId)
            ->where('provider', $provider)->where('enabled', true)->firstOrFail();
        $this->verifySignature($request, $setting);
        $timestamp = $request->header('X-Webhook-Timestamp');
        abort_if($timestamp && abs(now()->timestamp - (int) $timestamp) > 300, 409, 'Expired webhook timestamp.');
        $deliveryId = $request->header('X-Webhook-Id') ?: $request->header('X-Mailchimp-Webhook-Id');
        $idempotencyKey = hash('sha256', $provider.'|'.$businessId.'|'.
            ($deliveryId ?: $request->getContent()));

        try {
            $log = IntegrationSyncLog::create([
                'business_id' => $businessId, 'provider' => $provider, 'direction' => 'inbound',
                'operation' => (string) $request->input('event', $request->input('type', 'webhook')),
                'status' => 'processing', 'external_id' => $request->input('id'),
                'idempotency_key' => $idempotencyKey, 'payload' => $request->all(), 'attempts' => 1,
            ]);
        } catch (QueryException $exception) {
            if (in_array((string) $exception->getCode(), ['23000', '23505'], true)) {
                return response()->json(['accepted' => true, 'replayed' => true]);
            }
            throw $exception;
        }

        try {
            $result = match ($provider) {
                'facebook' => $this->facebookLead($request, $businessId),
                'whatsapp' => $this->whatsAppMessage($request, $businessId),
                'mailchimp' => $this->mailchimpActivity($request, $businessId),
                'jira' => $this->jiraIssue($request, $businessId),
            };
            $log->update(['status' => 'completed', 'completed_at' => now()]);
            return response()->json(['accepted' => true, 'record_id' => $result?->id]);
        } catch (\Throwable $exception) {
            $log->update(['status' => 'failed', 'error' => $exception->getMessage()]);
            report($exception);
            return response()->json(['accepted' => false], 422);
        }
    }

    private function facebookLead(Request $request, int $businessId): ?Lead
    {
        $email = $request->input('email');
        $externalId = $request->input('id');
        if ($externalId && IntegrationSyncLog::where('provider', 'facebook')->where('business_id', $businessId)
            ->where('external_id', $externalId)->where('status', 'completed')->exists()) return null;
        $stage = LeadStage::where('name', 'New Lead')->value('id');
        $leadValues = [
            'business_id' => $businessId, 'contact_owner_id' => null,
            'lead_source' => 'Facebook', 'topic' => $request->input('campaign_name', 'Facebook Campaign Lead'),
            'first_name' => $request->input('first_name', $request->input('name', 'Facebook')),
            'last_name' => $request->input('last_name', 'Lead'), 'stage_id' => $stage,
            'status' => 'active',
        ];
        $leadValues = array_merge($leadValues, $this->mappedValues(
            'facebook','lead',$businessId,$request->all(),
            ['topic','first_name','last_name','lead_source','stage_id','rating','job_title','status']
        ));
        $lead = Lead::withoutGlobalScopes()->create($leadValues);
        if ($request->integer('crm_campaign_id')) CampaignTarget::withoutGlobalScopes()->firstOrCreate(
            ['campaign_id'=>$request->integer('crm_campaign_id'),'target_type'=>Lead::class,'target_id'=>$lead->id],
            ['business_id'=>$businessId,'provider'=>'facebook','external_id'=>$externalId,'delivery_status'=>'imported','responded_at'=>now()]
        );
        return $lead;
    }

    private function whatsAppMessage(Request $request, int $businessId): Message
    {
        $statusPayload=data_get($request->all(),'entry.0.changes.0.value.statuses.0');
        if(is_array($statusPayload)){
            $externalId=$statusPayload['id']??null;$status=strtolower((string)($statusPayload['status']??'sent'));
            $link=IntegrationLink::withoutGlobalScopes()->where(['business_id'=>$businessId,'provider'=>'whatsapp','entity_type'=>'message','external_id'=>$externalId])->first();
            $message=$link?Message::withoutGlobalScopes()->where('business_id',$businessId)->find($link->entity_id):null;
            abort_unless($message,404,'WhatsApp message link was not found.');
            $values=['status'=>$status,'provider_metadata'=>$request->all()];
            if($status==='delivered')$values['delivered_at']=now();if($status==='read'){$values['delivered_at']=$message->delivered_at?:now();$values['read_at']=now();}if($status==='failed')$values['failed_at']=now();
            $message->update($values);$link->update(['external_status'=>$status,'metadata'=>$request->all()]);return $message;
        }
        $incoming=data_get($request->all(),'entry.0.changes.0.value.messages.0',[]);
        $values = [
            'business_id' => $businessId, 'type' => 'whatsapp',
            'subject' => $request->input('subject'), 'from_address' => $request->input('from',data_get($incoming,'from')),
            'to_addresses' => json_encode(Arr::wrap($request->input('to'))),
            'content' => $request->input('message',data_get($incoming,'text.body','')), 'status' => $request->input('status','delivered'),
            'conversation_id' => $request->input('conversation_id',data_get($incoming,'from',$request->input('id'))),
            'provider_message_id'=>data_get($incoming,'id',$request->input('id')),'provider_metadata'=>$request->all(),'sent_at' => now(),
        ];
        $values = array_merge($values,$this->mappedValues('whatsapp','message',$businessId,$request->all(),
            ['subject','from_address','content','status','conversation_id']));
        return Message::withoutGlobalScopes()->create($values);
    }

    private function mailchimpActivity(Request $request, int $businessId): Activity
    {
        $event = strtolower((string) $request->input('event', $request->input('type', 'campaign')));
        $email = $request->input('email', $request->input('data.email'));
        $externalId = $email ? md5(strtolower(trim($email))) : $request->input('id');
        if ($externalId) {
            $target = CampaignTarget::withoutGlobalScopes()->where('business_id',$businessId)
                ->where('provider','mailchimp')->where('external_id',$externalId)->first();
            if ($target) $target->update([
                'delivery_status'=>in_array($event,['bounce','cleaned','failed'],true)?'failed':'delivered',
                'response_status'=>$event,'responded_at'=>now(),
            ]);
            $contactEmail = ContactEmail::whereRaw('LOWER(email) = ?', [strtolower(trim((string) $email))])
                ->whereHas('contact', fn($query)=>$query->withoutGlobalScopes()->where('business_id',$businessId))->first();
            $contact = $contactEmail?->contact;
            if (!$contact && in_array($event, ['subscribe','subscribed','profile','upemail'], true) && $email) {
                $first = $request->input('data.merges.FNAME', $request->input('first_name', 'Mailchimp'));
                $last = $request->input('data.merges.LNAME', $request->input('last_name', 'Subscriber'));
                $values = [
                    'business_id'=>$businessId, 'first_name'=>$first, 'last_name'=>$last,
                    'type'=>'Customer', 'priority'=>'Medium', 'status'=>'active',
                    'description'=>'Synchronized inbound from Mailchimp',
                ];
                $values=array_merge($values,$this->mappedValues('mailchimp','contact',$businessId,$request->all(),
                    ['first_name','last_name','job_title','type','priority','department','description','stage_id','status']));
                $contact = Contact::withoutGlobalScopes()->create($values);
                ContactEmail::create(['contact_id'=>$contact->id,'email'=>$email]);
            }
            if ($contact) {
                $contactValues=$this->mappedValues('mailchimp','contact',$businessId,$request->all(),
                    ['first_name','last_name','job_title','type','priority','department','description','stage_id','status']);
                if(in_array($event,['unsubscribe','unsubscribed','cleaned'],true))$contactValues['status']='inactive';
                if(in_array($event,['subscribe','subscribed'],true))$contactValues['status']='active';
                if($contactValues)$contact->update($contactValues);
                IntegrationLink::withoutGlobalScopes()->updateOrCreate([
                    'business_id'=>$businessId,'provider'=>'mailchimp','entity_type'=>'contact',
                    'external_id'=>$externalId,
                ], ['entity_id'=>$contact->id,'external_status'=>$event,'metadata'=>$request->all()]);
            }
        }
        return Activity::withoutGlobalScopes()->create([
            'business_id' => $businessId, 'activity_type_id' => 10,
            'title' => 'Mailchimp: '.ucwords(str_replace('_', ' ', $event)),
            'subject' => $request->input('subject', $request->input('campaign_title')),
            'description' => $request->input('description', 'Mailchimp campaign activity'),
            'email_to' => $request->input('email'), 'to_recipients' => array_filter([$request->input('email')]),
            'email_status' => match ($event) { 'open','opened' => 'opened', 'reply','replied' => 'replied', 'bounce','failed' => 'failed', default => 'sent' },
            'sent_at' => now(), 'opened_at' => in_array($event, ['open','opened'], true) ? now() : null,
            'lifecycle_status' => 'completed', 'status' => 'active',
            'related_campaign_id' => $request->input('crm_campaign_id'),
            'owner_id' => $request->input('owner_id'),
        ]);
    }

    private function jiraIssue(Request $request, int $businessId): ?Ticket
    {
        $issueKey = $request->input('issue.key', $request->input('key', $request->input('id')));
        $link = IntegrationLink::withoutGlobalScopes()->where([
            'business_id'=>$businessId,'provider'=>'jira','entity_type'=>'ticket','external_id'=>$issueKey,
        ])->first();
        if (!$link) return null;

        $ticket = Ticket::withoutGlobalScopes()->where('business_id',$businessId)->find($link->entity_id);
        if (!$ticket) return null;
        $externalStatus = $request->input('issue.fields.status.name', $request->input('status'));
        $values = [];
        foreach (IntegrationFieldMapping::withoutGlobalScopes()->where([
            'business_id'=>$businessId,'provider'=>'jira','entity_type'=>'ticket','enabled'=>true,
        ])->get() as $mapping) {
            $value = data_get($request->all(), $mapping->external_field);
            if ($value === null || !in_array($mapping->crm_field, ['stage_id','priority_id','comments','topic','description'], true)) continue;
            $values[$mapping->crm_field] = data_get($mapping->value_map, (string) $value, $value);
        }
        if ($values) $ticket->update($values);
        $link->update(['external_status'=>$externalStatus,'metadata'=>$request->all()]);
        return $ticket;
    }

    private function verifySignature(Request $request, IntegrationSetting $setting): void
    {
        $secret = data_get($setting->credentials, 'webhook_secret');
        abort_if(! $secret, 403, 'Webhook secret is not configured.');
        $provided = (string) $request->header('X-CRM-Signature');
        $expected = hash_hmac('sha256', $request->getContent(), $secret);
        abort_unless($provided && hash_equals($expected, Str::after($provided, 'sha256=')), 403);
    }

    private function mappedValues(
        string $provider,
        string $entity,
        int $businessId,
        array $payload,
        array $allowed
    ): array {
        $values=[];
        foreach(IntegrationFieldMapping::withoutGlobalScopes()->where([
            'business_id'=>$businessId,'provider'=>$provider,'entity_type'=>$entity,'enabled'=>true,
        ])->get() as $mapping){
            if(!in_array($mapping->crm_field,$allowed,true))continue;
            $value=data_get($payload,$mapping->external_field);
            if($value===null)continue;
            $values[$mapping->crm_field]=data_get($mapping->value_map,(string)$value,$value);
        }
        return $values;
    }
}
