<?php

namespace App\Http\Livewire;

use Livewire\Component;
use Livewire\WithFileUploads;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
use Illuminate\Support\Facades\Storage;

use App\Http\Middleware\TenantMiddleware;
use App\Models\EmailMessage;
use App\Models\Member;

class EmailComunications extends Component
{
    use WithFileUploads;

    public ?int $messageId = null;
    public string $subject = '';
    public string $content_html = '';

    public array $recipients = [];

    public array $existingAttachments = [];
    public $newAttachments = [];

    public string $mode = 'now'; // 'now' | 'schedule'
    public ?string $schedule_at = null;
    public ?string $timezone = 'UTC';

    public $records;
    public $categories;
    public $courses;

    public bool $showForm = false;
    public bool $locked = false;

    public $success;
    public $error;

    public function boot()
    {
        app(TenantMiddleware::class)->setupTenantConnection();
    }

    public function mount()
    {
        if (auth()->user()?->level != env('LEVEL_ADMIN', 0)) {
            return redirect()->to('/dashboard');
        }

        $this->categories = [];
        $this->getCategories(\App\Models\Category::select('id', 'name')->where('parent_id', null)->orderBy('name')->get(), 0);

        $this->courses = [];
        $this->getCourses(\App\Models\Course::query()->where('parent_id', null)->orderBy('name', 'ASC')->get(), 0);
        usort($this->courses, function($a, $b) {
            return strcmp($a['name'], $b['name']);
        });

        $this->schedule_at = now($this->timezone)->addHour()->format('Y-m-d\TH:i');
    }

    public function render()
    {
        if (!$this->showForm) {
            $this->records = EmailMessage::withCount(['attachments', 'recipients'])
                ->orderBy('created_at', 'desc')
                ->get();
        } else {
            $this->categories = [];
            $this->getCategories(\App\Models\Category::select('id', 'name')->where('parent_id', null)->orderBy('name')->get(), 0);

            $this->courses = [];
            $this->getCourses(\App\Models\Course::query()->where('parent_id', null)->orderBy('name', 'ASC')->get(), 0);
            usort($this->courses, function($a, $b) {
                return strcmp($a['name'], $b['name']);
            });
        }

        return view('livewire.email_comunications');
    }

    protected function baseRules(): array
    {
        return [
            'subject' => 'required|string|max:255',
            'content_html' => 'required|string',
            'recipients' => 'required|array|min:1',
            'recipients.*.email_address' => 'required|email',
            'newAttachments' => 'nullable',
            'newAttachments.*' => 'file|max:20480',
        ];
    }

    protected function baseMessages(): array
    {
        return [
            'subject.*' => 'Il campo oggetto è richiesto',
            'content_html.*' => 'Il messaggio è richiesto',
            'recipients.*' => 'Selezionare almeno un destinatario',
            'schedule_at.after' => 'La data di invio deve essere nel futuro',
            'schedule_at.*' => 'Il campo data è richiesto',
        ];
    }

    protected function validateDraft(): void
    {
        $rules = [];
        // $rules = $this->baseRules();
        $rules['subject'] = 'required|string|max:255';
        $this->validate($rules, $this->baseMessages());
    }

    protected function validateSend(): void
    {
        $this->validate($this->baseRules(), $this->baseMessages());
    }

    protected function validateSchedule(): void
    {
        $rules = $this->baseRules();
        $rules['schedule_at'] = 'required|date|after:now';
        $this->validate($rules, $this->baseMessages());
    }

    public function add()
    {
        $this->reset(['messageId', 'subject', 'content_html', 'recipients', 'newAttachments', 'mode', 'schedule_at']);
        $this->mode = 'now';
        $this->schedule_at = now($this->timezone)->addHour()->format('Y-m-d\TH:i');
        $this->existingAttachments = [];

        $this->showForm = true;

        $this->dispatchBrowserEvent('load-editor', [
            'html'   => $this->content_html ?? '',
            'locked' => $this->locked,
        ]);
        $this->dispatchBrowserEvent('init-recipients-table', [
            'selected' => collect($this->recipients)->pluck('member_id')->filter()->values()->all(),
        ]);
    }

    public function edit($id)
    {
        try {
            $msg = EmailMessage::with(['recipients', 'attachments'])->findOrFail($id);

            $this->messageId = $msg->id;
            $this->subject = $msg->subject;
            $this->content_html = $msg->content_html;
            $this->recipients = $msg->recipients->map(fn($r) => [
                'member_id' => $r->member_id,
                'email_address' => $r->email_address,
                'first_name'    => optional($r->member)->first_name,
                'last_name'     => optional($r->member)->last_name,
            ])->toArray();
            usort($this->recipients, function($a, $b) {
                $last_name = strcmp($a['last_name'], $b['last_name']);
                $first_name = strcmp($a['first_name'], $b['first_name']);

                return $last_name == 0 ? $first_name : $last_name;
            });

            $this->mode = $msg->status === 'scheduled' ? 'schedule' : 'now';
            $this->schedule_at = optional($msg->schedule_at)?->setTimezone($this->timezone)?->format('Y-m-d\TH:i');
            $this->existingAttachments = $msg->attachments->map(fn($a) => [
                'id'   => $a->id,
                'name' => $a->name ?: basename($a->path),
                'size' => $a->size_human,
                'url'  => $a->public_url,
                'img'  => $a->is_image,
            ])->toArray();

            $this->showForm = true;
            $this->locked = $msg->isLocked();

            $this->dispatchBrowserEvent('load-editor', [
                'html' => $this->content_html ?? '',
                'locked' => $this->locked,
            ]);
            $this->dispatchBrowserEvent('init-recipients-table', [
                'selected' => collect($this->recipients)->pluck('member_id')->filter()->values()->all(),
            ]);
        } catch (\Throwable $ex) {
            $this->error = 'Errore (' . $ex->getMessage() . ')';
        }
    }


    public function duplicate($id, $withRecipients = true)
    {
        try {
            $copy = EmailMessage::with(['recipients', 'attachments'])->findOrFail($id)->duplicate($withRecipients);
            $this->edit($copy->id);
            $this->success = 'Bozza duplicata';
        } catch (\Throwable $ex) {
            $this->error = 'Errore (' . $ex->getMessage() . ')';
        }
    }

    public function saveDraft($html = null)
    {
        if ($html !== null) $this->content_html = $html;
        $this->validateDraft();

        DB::transaction(function () {
            $msg = $this->upsertMessage(status: 'draft', scheduleAt: null);
            $this->upsertRecipients($msg);
            $this->upsertAttachments($msg);
            $this->messageId = $msg->id;
            $this->locked = $msg->isLocked();
            $this->refreshAttachments($msg);
        });

        $this->success = 'Bozza salvata';

        $this->dispatchBrowserEvent('scroll-top');
        $this->dispatchBrowserEvent('load-editor', [
            'html'   => $this->content_html ?? '',
            'locked' => $this->locked,
        ]);
    }

    public function sendNow($html = null)
    {
        if ($html !== null) $this->content_html = $html;
        $this->validateSend();

        if ($this->messageId) {
            $existing = EmailMessage::findOrFail($this->messageId);
            if ($existing->isLocked()) {
                $this->error = 'Questa email è già in invio o inviata e non può essere modificata.';
                return;
            }
        }

        DB::transaction(function () {
            $msg = $this->upsertMessage(status: 'processing', scheduleAt: null);
            $this->upsertRecipients($msg, true);
            $this->upsertAttachments($msg, true);
            $this->messageId = $msg->id;
            $this->locked = true;
            $this->refreshAttachments($msg);
        });

        dispatch(new \App\Jobs\SendEmailMessage($this->messageId));
        $this->success = 'Invio avviato';

        $this->dispatchBrowserEvent('scroll-top');
        $this->dispatchBrowserEvent('load-editor', [
            'html'   => $this->content_html ?? '',
            'locked' => $this->locked,
        ]);
    }

    public function scheduleMessage($html = null)
    {
        if ($html !== null) $this->content_html = $html;
        $this->validateSchedule();

        if ($this->messageId) {
            $existing = EmailMessage::findOrFail($this->messageId);
            if ($existing->isLocked()) {
                $this->error = 'Questa email è già in invio o inviata e non può essere modificata.';
                return;
            }
        }

        $scheduledAt = \Carbon\Carbon::parse($this->schedule_at, $this->timezone)->setTimezone('UTC');

        DB::transaction(function () use ($scheduledAt) {
            $msg = $this->upsertMessage(status: 'scheduled', scheduleAt: $scheduledAt);
            $this->upsertRecipients($msg, true);
            $this->upsertAttachments($msg, true);
            $this->messageId = $msg->id;
            $this->locked = $msg->isLocked();
            $this->refreshAttachments($msg);
        });

        $this->success = 'Email programmata';

        $this->dispatchBrowserEvent('scroll-top');
        $this->dispatchBrowserEvent('load-editor', [
            'html'   => $this->content_html ?? '',
            'locked' => $this->locked,
        ]);
    }

    protected function upsertMessage(string $status, $scheduleAt): EmailMessage
    {
        return EmailMessage::updateOrCreate(
            ['id' => $this->messageId],
            [
                'subject'      => $this->subject,
                'content_html' => $this->content_html,
                'status'       => $status,
                'schedule_at'  => $scheduleAt,
                'created_by'   => auth()->id(),
            ]
        );
    }

    protected function upsertRecipients(EmailMessage $msg, bool $force = false): void
    {
        if (!$force && $msg->isLocked()) return;

        $msg->recipients()->delete();

        $rows = collect($this->recipients)->map(fn($r) => [
            'email_message_id' => $msg->id,
            'member_id' => $r['member_id'] ?? null,
            'email_address' => $r['email_address'],
            'status' => 'pending',
            'created_at' => now(),
            'updated_at' => now(),
        ])->values()->all();

        if ($rows) \App\Models\EmailMessageRecipient::insert($rows);
    }

    protected function upsertAttachments(EmailMessage $msg, bool $force = false): void
    {
        if (!$force && $msg->isLocked()) return;

        $files = is_array($this->newAttachments) ? $this->newAttachments : [$this->newAttachments];
        foreach ($files as $upload) {
            if (!$upload) continue;
            $path = $upload->store('emails/' . \Illuminate\Support\Str::uuid(), 'public');
            $msg->attachments()->create([
                'disk' => 'public',
                'path' => $path,
                'name' => $upload->getClientOriginalName(),
                'size' => $upload->getSize(),
            ]);
        }
    }

    public function cancel()
    {
        $this->showForm = false;
        $this->reset(['messageId', 'subject', 'content_html', 'recipients', 'newAttachments', 'mode', 'schedule_at']);
        $this->mode = 'now';
        $this->schedule_at = now($this->timezone)->addHour()->format('Y-m-d\TH:i');

        $this->dispatchBrowserEvent('init-archive-table');
    }

    public function getCategories($records, $indentation)
    {
        foreach ($records as $record) {
            $this->categories[] = array('id' => $record->id, 'name' => $record->getTree());
            if (count($record->childs))
                $this->getCategories($record->childs, $indentation + 1);
        }
    }

    public function getCourses($records, $indentation)
    {
        /** @var \App\Models\Course $record */
        foreach ($records as $record) {
            // $this->courses[] = array('id' => $record->id, 'name' => $record->getDetailsName());
            $this->courses[] = array('id' => $record->id, 'name' => ($record->discipline?->name ? $record->discipline->name . ' - ' : '') . $record->name);
            if (count($record->childs))
                $this->getCourses($record->childs, $indentation + 1);
        }
    }

    public function toggleRecipient($id)
    {
        $id = (int)$id;

        $idx = collect($this->recipients)->search(fn($r) => (int)($r['member_id'] ?? 0) === $id);
        if ($idx !== false) {
            array_splice($this->recipients, $idx, 1);
            return;
        }

        $m = Member::select('id', 'email', 'first_name', 'last_name')->find($id);
        if (!$m || empty($m->email)) return;

        $this->recipients[] = [
            'member_id' => $m->id,
            'email_address' => $m->email,
            'first_name' => $m->first_name,
            'last_name' => $m->last_name,
        ];

        usort($this->recipients, function($a, $b) {
            $last_name = strcmp($a['last_name'], $b['last_name']);
            $first_name = strcmp($a['first_name'], $b['first_name']);

            return $last_name == 0 ? $first_name : $last_name;
        });
    }

    public function removeNewAttachment(int $index): void
    {
        if ($this->locked) return;
        if (is_array($this->newAttachments) && array_key_exists($index, $this->newAttachments)) {
            array_splice($this->newAttachments, $index, 1);
        }
    }

    public function removeExistingAttachment(int $id): void
    {
        if ($this->locked || !$this->messageId) return;

        $att = \App\Models\EmailMessageAttachment::find($id);
        if (!$att || $att->email_message_id !== $this->messageId) return;

        try {
            if ($att->disk && $att->path) {
                $disk = Storage::disk($att->disk);
                $dir = dirname($att->path);
                $disk->delete($att->path);

                if (empty($disk->files($dir)) && empty($disk->directories($dir))) {
                    $disk->deleteDirectory($dir);
                }
            }
        } catch (\Throwable $e) {
        }

        $att->delete();

        $this->existingAttachments = array_values(array_filter(
            $this->existingAttachments,
            fn($a) => (int)$a['id'] !== (int)$id
        ));
    }


    protected function refreshAttachments(EmailMessage $msg): void
    {
        $this->existingAttachments = $msg->attachments()
            ->get()
            ->map(fn($a) => [
                'id'   => $a->id,
                'name' => $a->name ?: basename($a->path),
                'size' => $a->size_human,
                'url'  => $a->public_url,
                'img'  => $a->is_image,
            ])->toArray();

        $this->newAttachments = [];
    }

    public function deleteMessage(int $id)
    {
        $msg = \App\Models\EmailMessage::with(['attachments'])->findOrFail($id);

        if (! in_array($msg->status, ['draft', 'failed'], true)) {
            return;
        }

        foreach ($msg->attachments as $a) {
            try {
                Storage::disk($a->disk ?? 'public')->delete($a->path);
            } catch (\Throwable $e) {
            }
        }

        $msg->delete();

        $this->dispatchBrowserEvent('email-deleted', ['id' => $id]);
    }
}
