<?php

namespace App;

use App\Contracts\InteractsWithContentSync;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Mtc\Crm\Contracts\FormModel;
use Mtc\Crm\Models\EnquiryType;
use Mtc\Crm\Models\FormAction;
use Mtc\MercuryDataModels\Form;
use Mtc\MercuryDataModels\FormSection;
use Mtc\MercuryDataModels\FormQuestion;

class FormRepository extends \Mtc\Crm\FormRepository implements InteractsWithContentSync
{
    /**
     * Update form from request
     *
     * @param Request $request
     * @param FormModel $form
     * @return void
     */
    #[\Override]
    public function update(Request $request, FormModel $form)
    {
        DB::beginTransaction();
        $form->update([
            'name' => $request->input('name'),
            'is_active' => (bool)$request->input('is_active'),
            'data' => [
                'multistep' => (bool)$request->input('multistep'),
            ],
            'type_id' => $request->input('type_id'),
            'initial_status_id' => $request->input('initial_status_id'),
            'initial_assignee' => $request->input('initial_assignee'),
            'success_message' => $request->input('success_message'),
            'confirmation_message' => $request->input('confirmation_message'),
            'send_customer_confirmation_email' => $request->input('send_customer_confirmation_email') ?? true,
        ]);

        $this->syncActions(collect($request->input('action_data', [])), $form);
        $form->dealerships()->sync($request->input('dealership_ids', []));
        $this->dropOldSections($request->input('sections', []), $form);
        $this->dropOldQuestions($request->input('sections', []), $form);
        $this->saveSections(collect($request->input('sections', [])), $form);
        $this->setEmailRecipient($form);
        $this->setSubject($form);
        DB::commit();
    }

    #[\Override]
    public function copy(FormModel $form, string $title, bool $withContent = false): FormModel
    {
        $copy = parent::copy($form, $title, $withContent);
        $copy->dealerships()->sync($form->dealerships->pluck('id'));
        return $copy;
    }

    /**
     * Sync actions of the form
     *
     * @param Collection $actions
     * @param FormModel $form
     * @return void
     */
    #[\Override]
    protected function syncActions(Collection $actions, FormModel $form)
    {
        $enabledActions = $actions
            ->filter(fn($action) => $action['enabled'] ?? false);

        $form->actions()
            ->whereNotIn('action_name', $enabledActions->keys())
            ->delete();

        $enabledActions->each(fn($action, $name) => $form->actions()
            ->updateOrCreate([
                'action_name' => $name,
            ], [
                'fields' => collect($action['fields'])->mapWithKeys(fn($field, $key) => [$key => $field['value']]),
            ]));
    }

    private function saveSections(Collection $sections, FormModel $form)
    {
        $sections->each(function ($data) use ($form) {
            /** @var FormSection $section */
            if (!is_numeric($data['id'])) {
                $section = $form->sections()
                    ->create([
                        'name' => $data['name'],
                        'is_active' => $data['is_active'] ?? 0,
                        'settings' => $data['settings'] ?? [],
                        'segments' => collect($data['sections'])->pluck('name'),
                    ]);
            } else {
                $section = FormSection::query()->find($data['id']);
                $section->update([
                    'name' => $data['name'],
                    'is_active' => $data['is_active'] ?? 0,
                    'settings' => $data['settings'] ?? [],
                    'segments' => collect($data['sections'])->pluck('name'),
                ]);
            }

            collect($data['sections'])
                ->each(fn($segment) => collect($segment['questions'])
                    ->each(fn($question, $index) => $this->saveQuestion(
                        $question,
                        $index,
                        $section,
                        $form,
                        $segment['name']
                    )));
        });
    }

    private function saveQuestion($data, int $order, FormSection $section, FormModel $form, ?string $segment)
    {
        $data_column = $data['data'] ?? [];
        $data_column['component'] = $data['component'] ?? null;
        $data_column['componentName'] = $data['componentName'] ?? null;
        $data_column['meta'] = $data['meta'] ?? null;
        $data_column['cssId'] = $data['cssId'] ?? null;
        $data_column['cssClass'] = $data['cssClass'] ?? null;
        $data['data'] = $data_column;
        $type = $data['type'] ?? $data['fieldId'];
        $validation = $data['validation'] ?? [];

        if (in_array($type, ['email', 'email_field']) && in_array('email', $validation)) {
            $validation = array_map(
                fn($rule) => $rule === 'email' ? 'email:rfc,dns' : $rule,
                $validation
            );
        }

        if ($type == 'file_upload') {
            $validation = in_array('required', $validation)
                ? ['required', 'file']
                : ['nullable', 'file'];
            if (!empty($data['data']['meta']['options']['fileTypes'])) {
                $validation[] = 'mimes:' . $data['data']['meta']['options']['fileTypes'];
            }
            if (!empty($data['data']['meta']['options']['maxSize'])) {
                $validation[] = 'max:' . ($data['data']['meta']['options']['maxSize'] * 1024);
            }
        }

        if (!is_numeric($data['id'])) {
            $section->questions()
                ->create([
                    'form_id' => $form->id,
                    'order' => $order,
                    'segment' => $segment,
                    'name' => $data['name'],
                    'type' => $data['type'] ?? $data['fieldId'],
                    'is_active' => $data['is_active'] ?? 0,
                    'settings' => $data['settings'] ?? [],
                    'validation' => $validation,
                    'description' => $data['description'] ?? '',
                    'placeholder' => $data['placeholder'] ?? '',
                    'answer_options' => $data['answer_options'] ?? [],
                    'data' => $data['data'] ?? [],
                    'is_enquiry_subject' => $data['is_enquiry_subject'] ?? false,
                    'is_customers_email' => $data['is_customers_email'] ?? false,
                ]);
        } else {
            FormQuestion::query()->updateOrCreate([
                'id' => $data['id']
            ], [
                'form_section_id' => $section->id,
                'form_id' => $form->id,
                'order' => $order,
                'segment' => $segment,
                'name' => $data['name'],
                'type' => $data['fieldId'],
                'is_active' => $data['is_active'] ?? 0,
                'settings' => $data['settings'] ?? [],
                'validation' => $validation,
                'description' => $data['description'] ?? '',
                'placeholder' => $data['placeholder'] ?? '',
                'answer_options' => $data['answer_options'] ?? [],
                'data' => $data['data'] ?? [],
                'is_enquiry_subject' => $data['is_enquiry_subject'] ?? false,
                'is_customers_email' => $data['is_customers_email'] ?? false,
            ]);
        }
    }

    private function dropOldSections(array $sectionData, FormModel $form)
    {
        $sectionIds = collect($sectionData)
            ->pluck('id')
            ->filter(fn($id) => is_numeric($id));

        $form->sections()->whereNotIn('id', $sectionIds)->delete();
    }

    private function dropOldQuestions(array $sectionData, FormModel $form)
    {
        $questionIds = collect([]);
        collect($sectionData)
            ->each(fn($section) => collect($section['sections'] ?? [])
                ->each(fn($section) => collect($section['questions'] ?? [])
                    ->filter(fn($question) => is_numeric($question['id']))
                    ->each(fn($question) => $questionIds->push($question['id']))));

        $form->questions()->whereNotIn('id', $questionIds)->delete();
    }

    private function setEmailRecipient(FormModel $form): void
    {
        $hasCustomerEmailFlag = false;
        $emailQuestion = null;
        $form->load('sections.questions');

        $form->sections->each(function ($section) use (&$hasCustomerEmailFlag, &$emailQuestion) {
            $section->questions->each(function ($question) use (&$hasCustomerEmailFlag, &$emailQuestion) {
                if ($question->is_customer_email) {
                    $hasCustomerEmailFlag = true;
                }

                if (is_null($emailQuestion) && in_array($question->type, ['email', 'email_field'])) {
                    $emailQuestion = $question;
                }
            });
        });

        if ($emailQuestion && !$hasCustomerEmailFlag) {
            $emailQuestion->is_customers_email = 1;
            $emailQuestion->save();
        }
    }

    private function setSubject(FormModel $form): void
    {
        $form->load('sections.questions');

        $hasEnquirySubject = $form->sections
            ->contains(fn($section) => $section->questions->contains('is_enquiry_subject', true));

        if ($hasEnquirySubject) {
            return;
        }
        $subject_values = [
            'first name',
            'last name',
            'full name'
        ];

        $form->questions->each(fn ($question) => $question->update([
            'is_enquiry_subject' => in_array(strtolower((string) $question->name), $subject_values)
        ]));
    }

    public function importRecord(array $entry): bool
    {
        $form = Form::query()
            ->create(Arr::only($entry, new Form()->getFillable()));

        if (!empty($entry['sections'])) {
            foreach ($entry['sections'] as $sectionEntry) {
                $section = FormSection::query()->create(
                    array_merge(
                        Arr::except($sectionEntry, ['id', 'form_id']),
                        ['form_id' => $form->id]
                    )
                );

                if (!empty($sectionEntry['questions'])) {
                    foreach ($sectionEntry['questions'] as $questionEntry) {
                        FormQuestion::query()->create(
                            array_merge(
                                Arr::except($questionEntry, ['id', 'form_id', 'form_section_id']),
                                [
                                'form_id' => $form->id,
                                'form_section_id' => $section->id,
                                ]
                            )
                        );
                    }
                }
            }
        }

        if (!empty($entry['actions'])) {
            foreach ($entry['actions'] as $actionEntry) {
                $formAction = new FormAction();
                $formAction->fill(Arr::except($actionEntry, ['id', 'form_id']));
                $formAction->form_id = $form->id;
                $formAction->save();
            }
        }

        if (!empty($entry['type'])) {
            $enquiryType = EnquiryType::firstOrCreate(
                ['name' => $entry['type']['name']],
                Arr::only($entry['type'], new EnquiryType()->getFillable())
            );

            $form->type_id = $enquiryType->id;
            $form->save();
        }

        return true;
    }

    public function canBeImported(array $entry): bool
    {
        return !Form::query()
            ->where('name', $entry['name'])
            ->exists();
    }

    public function exportToRemote(array $selections): array
    {
        return Form::query()
            ->with([
                'sections.questions',
                'actions',
                'type'
            ])
            ->whereIn('id', $selections)
            ->get()
            ->toArray();
    }

    public function checkImportEntryValidity(array $dataEntry, array $allEntries): array
    {
        $errors = [];
        if (Form::query()->where('name', $dataEntry['name'])->exists()) {
            $errors[] = __('validation.import_name_taken', ['name' => $dataEntry['name']]);
        }

        return [
            'data' => $dataEntry,
            'errors' => $errors,
        ];
    }
}
