<?php

namespace App\Crm;

use App\Facades\Settings;
use App\Mail\EmailWhitelistFilter;
use App\Traits\LoadsDataFromSource;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Mtc\Crm\Contracts\EnquiryActionModel;
use Mtc\Crm\Contracts\EnquiryModel;
use Mtc\MercuryDataModels\Dealership;

class SendCopyViaMail extends \Mtc\Crm\Actions\SendCopyViaMail
{
    use LoadsDataFromSource;
    use CheckForExpiredAction;

    private string $error;

    private array $recipients = [];

    private array $sentRecipients = [];

    private array $skippedRecipients = [];

    private EnquiryModel $originalEnquiry;

    public function enabled(): bool
    {
        return true;
    }

    /**
     * Data fields to add to form
     *
     * @return array
     */
    #[\Override]
    public function formAdditionalDataFields(): array
    {
        return [
            'send_to_field_location' => [
                'type' => 'select',
                'source' => 'form-questions',
                'validation' => [],
                'label' => 'Question that specifies where to send submission'
            ],
            'send_to_both' => [
                'type' => 'checkbox',
                'validation' => [
                ],
                'label' => 'Send to both custom email field and action email'
            ],
            'recipient' => [
                'type' => 'text',
                'validation' => [
                    'required',
                    'email',
                ],
                'label' => 'Send Email To'
            ],
            'recipient_partial' => [
                'type' => 'text',
                'validation' => ['nullable', 'email'],
                'label' => 'Partial submissions email (if different from main recipient)'
            ],
            'recipient_full' => [
                'type' => 'text',
                'validation' => ['nullable', 'email'],
                'label' => 'Full submissions email (if different from main recipient)'
            ],
            'enquiry_subject' => [
                'type' => 'text',
                'validation' => 'required',
                'label' => 'Enquiry Email Subject',
            ],
            'enquiry_content' => [
                'type' => 'textarea',
                'label' => 'Enquiry Email Message',
            ],
        ];
    }

    /**
     * Handle the action
     *
     * @param EnquiryActionModel $action
     * @param EnquiryModel $enquiry
     * @return bool
     */
    #[\Override]
    public function handle(EnquiryActionModel $action, EnquiryModel $enquiry): ?bool
    {
        if ($this->expired($action)) {
            return null;
        }

        // Clone the enquiry to leave the original intact
        $clonedEnquiry = clone $enquiry;
        $clonedEnquiry->details = $this->adjustEnquiryDetails($enquiry);

        try {
            $this->action = $action;
            $this->originalEnquiry = $enquiry;
            $emails = $this->determineRecipients($clonedEnquiry, $action);
            $this->recipients = $emails;
            if (!empty($emails)) {
                $partition = app(EmailWhitelistFilter::class)->partitionRecipients($emails);
                $this->sentRecipients = $partition['sent'];
                $this->skippedRecipients = $partition['skipped'];

                $class = config('crm.new_enquiry_email');
                collect($emails)->each(fn ($email) => Mail::to($email)->send(new $class($clonedEnquiry)));
                return true;
            }
        } catch (\Exception $exception) {
            $this->error = $exception->getMessage();
            Log::error($exception->getMessage(), [
                'tenant' => tenant('id'),
                'emails' => $emails,
                'exception' => $exception,
            ]);
        }
        return false;
    }

    #[\Override]
    public function successDetails(): string
    {
        $sent = !empty($this->sentRecipients)
            ? implode(', ', $this->sentRecipients)
            : 'none';

        $message = "Copy sent to {$sent}";

        if (!empty($this->skippedRecipients)) {
            $message .= '; skipped due to whitelist: ' . implode(', ', $this->skippedRecipients);
        }

        return $message;
    }

    #[\Override]
    public function failureReason(): string
    {
        if (empty($this->recipients)) {
            return 'no recipient specified';
        }
        return $this->error ?? 'issue sending mail';
    }

    private function adjustEnquiryDetails(EnquiryModel $enquiry): array
    {
        $details = $enquiry->getNormalizedAnswers()->map(fn ($detail) => (array) $detail)->toArray();

        foreach ($details as $key => $detail) {
            $details[$key]['question'] = strip_tags((string) $details[$key]['question']);
            $question = $enquiry
                ->form
                ->questionsInclTrashed->where('id', $detail['question_id'])
                ->first();

            // Answer is an ID of a data source
            $source = $question?->data['meta']['options']['source'] ?? null;
            if (!empty($source) && is_numeric($detail['answer'])) {
                $details[$key]['original_answer'] = $detail['answer'];
                $details[$key]['answer'] = $this->getChoicesFromDataSource($source)
                    ->filter(fn ($row) => $row->id == $detail['answer'])
                    ->first()
                    ?->name ?? __('labels.unknown');
            }

            // If question is "Select Branch" or "Dealer", rename + look up dealership
            if (in_array($detail['question'], ['Select Branch', 'Dealership']) && is_numeric($detail['answer'])) {
                $dealership = Dealership::query()->find((int)$detail['answer']);
                if (!empty($dealership)) {
                    $details[$key]['question'] = 'Dealership';
                    $details[$key]['original_answer'] = $detail['answer'];
                    $details[$key]['answer'] = $dealership->name;
                }
            }
        }

        return $details;
    }

    private function determineRecipients(EnquiryModel $enquiry, EnquiryActionModel $action): array
    {
        $sendToBoth = $action->data['send_to_both'] ?? false;
        $emails = $this->emailsSpecifiedByQuestions($enquiry);

        if ($this->action->data['send_to_field_location'] ?? null) {
            $emails = $this->emailsSpecifiedInLocationField($emails, $enquiry);
        }

        if ($sendToBoth || empty($emails)) {
            $emails = array_merge($emails, $this->emailsSpecifiedInMainField());
        }

        $emails = array_merge(
            $emails,
            $this->enquiryVehicleRecipients($enquiry),
        );

        return array_unique($emails);
    }

    private function enquiryVehicleRecipients(EnquiryModel $enquiry): array
    {
        $enquiry_vehicles = $enquiry->objects->filter(fn ($object) => $object->object_type === 'vehicle');

        return $enquiry_vehicles->count() && Settings::get('form-enquiry-send-to-vehicle-dealership')
            ? explode(',', $enquiry_vehicles->first()?->object?->dealership?->email ?? '')
            : [];
    }

    private function emailsSpecifiedInLocationField(array $emails, EnquiryModel $enquiry): array
    {
        $specifiedField = $this->action->data['send_to_field_location'] ?? null;

        $answer = $enquiry->getNormalizedAnswers()
            ->filter(fn ($detail) => $detail['question'] === $specifiedField)
            ->first();

        if (!$answer) {
            return array_filter($emails, fn ($email) => $email && filter_var($email, FILTER_VALIDATE_EMAIL));
        }

        // Check if this question is a dealership select with email field configured
        // Use original enquiry to access form relationship (clone may not have it loaded)
        $this->originalEnquiry->load('form.questions');
        $question = $this->originalEnquiry->form->questions
            ->where('name', $specifiedField)
            ->first();

        $source = $question?->data['meta']['options']['source'] ?? null;
        $emailField = $question?->data['meta']['options']['emailField'] ?? null;

        // Use original_answer if available (set by adjustEnquiryDetails when answer was an ID)
        $answerValue = $answer['original_answer'] ?? $answer['answer'];

        if ($source === 'dealerships' && $emailField && is_numeric($answerValue)) {
            $email = $this->getDealershipEmail((int) $answerValue, $emailField);
            if ($email) {
                $emails[] = $email;
            }
        } else {
            $emails[] = $answer['answer'] ?? null;
        }

        return array_filter($emails, fn ($email) => $email && filter_var($email, FILTER_VALIDATE_EMAIL));
    }

    /**
     * Get email from dealership based on the configured email field
     */
    private function getDealershipEmail(int $dealershipId, string $emailField): ?string
    {
        $dealership = Dealership::query()
            ->with('attributeValues')
            ->find($dealershipId);

        if (!$dealership) {
            return null;
        }

        if ($emailField === 'email') {
            return $dealership->email;
        }

        if (str_starts_with($emailField, 'attribute:')) {
            $attributeSlug = str_replace('attribute:', '', $emailField);
            $attributeValue = $dealership->attributeValues
                ->firstWhere('slug', $attributeSlug);

            return $attributeValue?->value_text ?? $attributeValue?->value;
        }

        if (str_starts_with($emailField, 'department:')) {
            $departmentName = str_replace('department:', '', $emailField);

            return $dealership->departments()
                ->where('name', $departmentName)
                ->whereNotNull('email')
                ->where('email', '!=', '')
                ->first()
                ?->email;
        }

        return null;
    }

    /**
     * Extracts and validates email addresses specified in the main field.
     *
     * @return array Returns an array of valid email addresses.
     */
    private function emailsSpecifiedInMainField(): array
    {
        $actionRecipient = $this->getRecipientForEnquiryStatus();

        if (empty($actionRecipient)) {
            return [];
        }

        $emails = array_map(trim(...), explode(',', (string) $actionRecipient));

        $validEmails = array_filter($emails, fn ($email) => filter_var($email, FILTER_VALIDATE_EMAIL));

        return $validEmails;
    }

    private function getRecipientForEnquiryStatus(): ?string
    {
        $isPartial = $this->originalEnquiry->status_id === 0;

        if ($isPartial && !empty($this->action->data['recipient_partial'])) {
            return $this->action->data['recipient_partial'];
        }

        if (!$isPartial && !empty($this->action->data['recipient_full'])) {
            return $this->action->data['recipient_full'];
        }

        return $this->action->data['recipient'] ?? null;
    }

    private function emailsSpecifiedByQuestions(EnquiryModel $enquiry): array
    {
        $questionsSpecifyField = $enquiry->form->questions
            ->filter(fn ($question) => $question->data['specifies-recipient'] ?? false)
            ->keyBy('id');

        if ($questionsSpecifyField->isEmpty()) {
            return [];
        }

        $questionsSpecifyFieldIds = $questionsSpecifyField->pluck('id')->toArray();
        return $enquiry->getNormalizedAnswers()
            ->filter(fn ($answer) => in_array($answer['question_id'], $questionsSpecifyFieldIds))
            ->map(fn ($answer) => $this->extractEmailsFromAnswerValue($answer, $questionsSpecifyField))
            ->flatten(1)
            ->filter()
            ->toArray();
    }

    private function extractEmailsFromAnswerValue(array $answer, Collection $fieldMap): array
    {
        $valueArray = explode("\n", $fieldMap[$answer['question_id']]->data['field-emails-specification'] ?? '');
        $specifiedValue = collect($valueArray)
            ->map(fn ($row) => explode(':', $row))
            ->filter(fn ($row) => array_key_exists('original_answer', $answer)
                    ? $row[0] == $answer['original_answer']
                    : $row[0] == $answer['answer'])
            ->map(fn ($row) => $row[1])
            ->first();

        return collect(explode(',', (string) $specifiedValue))
            ->map(fn ($email) => trim($email))
            ->filter(fn ($email) => filter_var($email, FILTER_VALIDATE_EMAIL))
            ->toArray();
    }
}
