<?php

declare(strict_types=1);

namespace ABS\Modules\Agenda\Controllers;

use ABS\Core\Controller;
use ABS\Modules\Agenda\Repositories\AgendaRepository;
use ABS\Modules\Broker\Repositories\BrokerRepository;
use ABS\Modules\Programari\Repositories\AppointmentRepository;
use ABS\Modules\SPVInvoices\Repositories\SpvInvoiceRepository;
use ABS\Modules\Users\Repositories\UserRepository;

final class AgendaController extends Controller
{
    private AgendaRepository $repository;

    public function __construct()
    {
        $this->repository = new AgendaRepository();
    }

    public function events(): never
    {
        $filters = array_values(array_filter(array_map('trim', explode(',', (string) ($_GET['types'] ?? ''))), static fn(string $value): bool => $value !== ''));
        $filters = $filters === [] ? ['insurance', 'invoice_due', 'birthday', 'activity', 'appointment'] : $filters;

        $all = $this->collectEvents($filters);

        $this->json(['events' => $all]);
    }

    public function alerts(): never
    {
        $events = $this->collectEvents(['insurance', 'invoice_due', 'birthday', 'activity', 'appointment']);
        $now = strtotime(date('Y-m-d H:i:s'));
        $inDay = $now + 86400;
        $inTwoHours = $now + 7200;
        $alerts = [];

        foreach ($events as $event) {
            $start = trim((string) ($event['start'] ?? ''));
            if ($start === '') {
                continue;
            }

            $eventTs = strtotime($start);
            if ($eventTs === false || $eventTs < $now || $eventTs > $inDay) {
                continue;
            }

            $alerts[] = [
                'id' => (string) ($event['id'] ?? ''),
                'title' => (string) ($event['title'] ?? 'Eveniment'),
                'start' => $start,
                'type' => (string) (($event['extendedProps']['type'] ?? 'activity')),
                'severity' => $eventTs <= $inTwoHours ? 'high' : 'medium',
            ];
        }

        usort($alerts, static fn(array $a, array $b): int => strcmp((string) ($a['start'] ?? ''), (string) ($b['start'] ?? '')));

        $this->json(['alerts' => $alerts]);
    }

    public function create(): never
    {
        $created = $this->repository->create([
            'title' => trim((string) ($_POST['title'] ?? 'Eveniment nou')),
            'start' => trim((string) ($_POST['start'] ?? date('Y-m-d'))),
            'end' => trim((string) ($_POST['end'] ?? '')),
            'all_day' => !empty($_POST['all_day']),
            'type' => trim((string) ($_POST['type'] ?? 'activity')),
            'subtype' => trim((string) ($_POST['subtype'] ?? '')),
            'notes' => trim((string) ($_POST['notes'] ?? '')),
        ]);

        $this->json([
            'success' => true,
            'event' => $this->toCalendarEvent($created),
        ]);
    }

    public function update(string $id): never
    {
        $updated = $this->repository->update($id, [
            'title' => trim((string) ($_POST['title'] ?? 'Eveniment')),
            'start' => trim((string) ($_POST['start'] ?? date('Y-m-d'))),
            'end' => trim((string) ($_POST['end'] ?? '')),
            'all_day' => !empty($_POST['all_day']),
            'type' => trim((string) ($_POST['type'] ?? 'activity')),
            'subtype' => trim((string) ($_POST['subtype'] ?? '')),
            'notes' => trim((string) ($_POST['notes'] ?? '')),
        ]);

        $this->json([
            'success' => $updated !== null,
            'event' => $updated !== null ? $this->toCalendarEvent($updated) : null,
        ]);
    }

    public function move(string $id): never
    {
        $updated = $this->repository->update($id, [
            'start' => trim((string) ($_POST['start'] ?? date('Y-m-d'))),
            'end' => trim((string) ($_POST['end'] ?? '')),
            'all_day' => !empty($_POST['all_day']),
        ]);

        $this->json(['success' => $updated !== null]);
    }

    public function delete(string $id): never
    {
        $this->json(['success' => $this->repository->delete($id)]);
    }

    private function toCalendarEvent(array $event): array
    {
        $type = (string) ($event['type'] ?? 'activity');
        $source = (string) ($event['source'] ?? 'manual');
        $sourceModule = (string) ($event['source_module'] ?? '');
        $editable = $source === 'manual' || $sourceModule === 'programari';
        $status = (string) ($event['status'] ?? '');

        return [
            'id' => (string) ($event['id'] ?? ''),
            'title' => (string) ($event['title'] ?? 'Eveniment'),
            'start' => (string) ($event['start'] ?? date('Y-m-d')),
            'end' => (string) ($event['end'] ?? ''),
            'allDay' => !empty($event['all_day']),
            'backgroundColor' => $this->colorForType($type, $status),
            'borderColor' => $this->colorForType($type, $status),
            'extendedProps' => [
                'type' => $type,
                'subtype' => (string) ($event['subtype'] ?? ''),
                'notes' => (string) ($event['notes'] ?? ''),
                'source' => $source,
                'source_module' => $sourceModule,
                'source_record_id' => (string) ($event['source_record_id'] ?? ''),
                'status' => $status,
                'editable' => $editable,
            ],
            'editable' => $editable,
        ];
    }

    private function importInsuranceEvents(): array
    {
        $events = [];
        $policies = (new BrokerRepository())->all();

        foreach ($policies as $policy) {
            $date = $this->firstNonEmpty($policy, ['expiry_date', 'expires_at', 'end_date', 'expiration_date', 'valid_to', 'data_expirare']);
            if (!$this->isDate($date)) {
                continue;
            }

            $client = $this->firstNonEmpty($policy, ['client_name', 'client', 'nume_client', 'insured_name']);
            $number = $this->firstNonEmpty($policy, ['policy_number', 'no_polita', 'number', 'polita']);

            $events[] = $this->toCalendarEvent([
                'id' => 'import-insurance-' . md5(json_encode($policy, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)),
                'title' => trim('Asigurare: ' . ($client !== '' ? $client : 'Client') . ($number !== '' ? ' (' . $number . ')' : '')),
                'start' => $date,
                'all_day' => true,
                'type' => 'insurance',
                'notes' => 'Preluat automat din modulul Broker Asigurari.',
                'source' => 'imported',
            ]);
        }

        return $events;
    }

    private function importInvoiceDueEvents(): array
    {
        $events = [];
        $invoices = (new SpvInvoiceRepository())->invoices();

        foreach ($invoices as $invoice) {
            $dueDate = trim((string) ($invoice['due_date'] ?? ''));
            if (!$this->isDate($dueDate)) {
                continue;
            }

            $supplier = trim((string) ($invoice['supplier_name'] ?? 'Furnizor'));
            $series = trim((string) ($invoice['series'] ?? ''));
            $number = trim((string) ($invoice['number'] ?? ''));

            $events[] = $this->toCalendarEvent([
                'id' => 'import-invoice-' . (string) ($invoice['id'] ?? md5($dueDate . $supplier . $number)),
                'title' => trim('Scadenta factura: ' . $supplier . ' ' . $series . ' ' . $number),
                'start' => $dueDate,
                'all_day' => true,
                'type' => 'invoice_due',
                'notes' => 'Preluat automat din Facturi electronice SPV.',
                'source' => 'imported',
            ]);
        }

        return $events;
    }

    private function importBirthdayEvents(): array
    {
        $events = [];
        $users = (new UserRepository())->all();

        foreach ($users as $user) {
            $birthDate = $this->firstNonEmpty($user, ['birth_date', 'birthday', 'date_of_birth']);
            if (!$this->isDate($birthDate)) {
                continue;
            }

            $date = date('Y') . '-' . date('m-d', strtotime($birthDate));
            $fullName = trim((string) (($user['last_name'] ?? '') . ' ' . ($user['first_name'] ?? '')));

            $events[] = $this->toCalendarEvent([
                'id' => 'import-birthday-' . (string) ($user['id'] ?? md5($fullName . $date)),
                'title' => 'Zi nastere: ' . ($fullName !== '' ? $fullName : 'Utilizator'),
                'start' => $date,
                'all_day' => true,
                'type' => 'birthday',
                'notes' => 'Preluat automat din modulul Utilizatori.',
                'source' => 'imported',
            ]);
        }

        return $events;
    }

    private function importAppointments(): array
    {
        $events = [];
        $appointments = (new AppointmentRepository())->all();

        foreach ($appointments as $appointment) {
            $start = trim((string) ($appointment['starts_at'] ?? ''));
            if ($start === '') {
                continue;
            }

            $end = trim((string) ($appointment['ends_at'] ?? ''));
            $client = trim((string) ($appointment['client_name'] ?? 'Client'));
            $service = trim((string) ($appointment['service_type'] ?? 'programare'));

            $events[] = $this->toCalendarEvent([
                'id' => (string) ($appointment['id'] ?? ('app-' . md5($start . $client . $service))),
                'title' => 'Programare ' . $service . ': ' . $client,
                'start' => $this->normalizeDateTime($start),
                'end' => $this->normalizeDateTime($end),
                'all_day' => false,
                'type' => 'appointment',
                'subtype' => $service,
                'notes' => trim((string) ($appointment['notes'] ?? '')),
                'status' => trim((string) ($appointment['status'] ?? 'programata')),
                'source' => 'imported',
                'source_module' => 'programari',
                'source_record_id' => (string) ($appointment['id'] ?? ''),
            ]);
        }

        return $events;
    }

    private function collectEvents(array $filters): array
    {
        $manual = array_map(fn(array $event): array => $this->toCalendarEvent($event), $this->repository->all());
        $imported = array_merge(
            $this->importInsuranceEvents(),
            $this->importInvoiceDueEvents(),
            $this->importBirthdayEvents(),
            $this->importAppointments()
        );

        return array_values(array_filter(array_merge($manual, $imported), static fn(array $event): bool => in_array((string) (($event['extendedProps']['type'] ?? 'activity')), $filters, true)));
    }

    private function firstNonEmpty(array $payload, array $keys): string
    {
        foreach ($keys as $key) {
            if (!array_key_exists($key, $payload)) {
                continue;
            }

            $value = trim((string) $payload[$key]);
            if ($value !== '') {
                return $value;
            }
        }

        return '';
    }

    private function isDate(string $value): bool
    {
        if ($value === '') {
            return false;
        }

        $timestamp = strtotime($value);

        return $timestamp !== false;
    }

    private function colorForType(string $type, string $status = ''): string
    {
        if ($type === 'appointment') {
            return match ($status) {
                'confirmata' => '#2563eb',
                'in_lucru' => '#f59e0b',
                'finalizata' => '#16a34a',
                'anulata' => '#6b7280',
                default => '#7c3aed',
            };
        }

        return match ($type) {
            'insurance' => '#0284c7',
            'invoice_due' => '#dc2626',
            'birthday' => '#16a34a',
            default => '#334155',
        };
    }

    private function normalizeDateTime(string $value): string
    {
        $value = trim($value);
        if ($value === '') {
            return '';
        }

        if (preg_match('/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/', $value)) {
            return $value;
        }

        if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/', $value)) {
            return str_replace(' ', 'T', $value);
        }

        if (preg_match('/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/', $value)) {
            return $value . ':00';
        }

        if (preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/', $value)) {
            return str_replace(' ', 'T', $value) . ':00';
        }

        return $value;
    }
}
