<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Representante;
use App\Models\Reunion;
use App\Support\HistorialLogger;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use Illuminate\Validation\ValidationException;
use OpenSpout\Common\Entity\Row;
use OpenSpout\Common\Entity\Style\Style;
use OpenSpout\Writer\XLSX\Writer as XLSXWriter;

class ReunionController extends Controller
{
    public function index(Request $request)
    {
        $query = Reunion::query()->withCount(['representantes', 'actas']);

        if ($request->filled('tipo_reunion')) {
            $query->where('tipo_reunion', $request->string('tipo_reunion'));
        }

        if ($request->filled('modalidad')) {
            $query->where('modalidad', $request->string('modalidad'));
        }

        if ($request->filled('desde')) {
            $query->whereDate('fecha', '>=', $request->string('desde'));
        }

        if ($request->filled('hasta')) {
            $query->whereDate('fecha', '<=', $request->string('hasta'));
        }

        $perPage = (int) $request->integer('per_page', 50);
        if ($perPage < 1) {
            $perPage = 1;
        }
        if ($perPage > 500) {
            $perPage = 500;
        }

        $paginator = $query->orderByDesc('fecha')->paginate($perPage);

        return response()->json([
            'data' => $paginator->items(),
            'meta' => [
                'current_page' => $paginator->currentPage(),
                'per_page' => $paginator->perPage(),
                'last_page' => $paginator->lastPage(),
                'total' => $paginator->total(),
            ],
        ]);
    }

    public function export(Request $request)
    {
        $query = Reunion::query()->withCount(['representantes', 'actas']);

        if ($request->filled('tipo_reunion')) {
            $query->where('tipo_reunion', $request->string('tipo_reunion'));
        }

        if ($request->filled('modalidad')) {
            $query->where('modalidad', $request->string('modalidad'));
        }

        if ($request->filled('desde')) {
            $query->whereDate('fecha', '>=', $request->string('desde'));
        }

        if ($request->filled('hasta')) {
            $query->whereDate('fecha', '<=', $request->string('hasta'));
        }

        $fileName = 'reuniones.xlsx';

        return response()->streamDownload(function () use ($query) {
            $writer = new XLSXWriter();
            $writer->openToFile('php://output');
            $writer->getCurrentSheet()->setName('Reuniones');

            $headerStyle = (new Style())->setFontBold();
            $writer->addRow(Row::fromValues([
                'ID',
                'Tipo',
                'Fecha',
                'Hora',
                'Modalidad',
                'Lugar',
                'Enlace',
                'Frecuencia',
                'Representantes',
                'Actas',
            ], $headerStyle));

            $query->orderByDesc('fecha')->chunk(500, function ($rows) use ($writer) {
                foreach ($rows as $row) {
                    $writer->addRow(Row::fromValues([
                        (string) ($row->id_reunion ?? ''),
                        (string) ($row->tipo_reunion ?? ''),
                        (string) ($row->fecha ?? ''),
                        (string) ($row->hora ?? ''),
                        (string) ($row->modalidad ?? ''),
                        (string) ($row->lugar ?? ''),
                        (string) ($row->enlace ?? ''),
                        (string) ($row->frecuencia ?? ''),
                        (int) ($row->representantes_count ?? 0),
                        (int) ($row->actas_count ?? 0),
                    ]));
                }
            });

            $writer->close();
        }, $fileName, ['Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']);
    }

    public function store(Request $request)
    {
        $data = $request->validate([
            'tipo_reunion' => [
                'required',
                'string',
                'max:100',
                Rule::exists('tipos_reunion', 'nombre_tipo')->where('estado', 'activo'),
            ],
            'fecha' => ['required', 'date'],
            'hora' => ['nullable', 'string'],
            'modalidad' => ['required', 'in:Presencial,Virtual'],
            'lugar' => ['nullable', 'string', 'max:255'],
            'enlace' => ['nullable', 'string'],
            'frecuencia' => ['nullable', 'string', 'max:100'],
        ]);

        $data['tipo_reunion'] = trim((string) $data['tipo_reunion']);
        $data['hora'] = $this->normalizeHora($data['hora'] ?? null);

        $reunion = Reunion::create($data);

        HistorialLogger::log(
            $request->user()?->getKey(),
            'reunion_creada',
            'reuniones',
            (string) $reunion->getKey(),
            'Creación de reunión',
            null,
            $reunion->toArray()
        );

        return response()->json($reunion, 201);
    }

    public function show(Reunion $reunion)
    {
        return response()->json($reunion->load(['actas', 'representantes']));
    }

    public function update(Request $request, Reunion $reunion)
    {
        $antes = $reunion->toArray();

        $data = $request->validate([
            'tipo_reunion' => [
                'sometimes',
                'required',
                'string',
                'max:100',
                Rule::exists('tipos_reunion', 'nombre_tipo')->where('estado', 'activo'),
            ],
            'fecha' => ['sometimes', 'required', 'date'],
            'hora' => ['sometimes', 'nullable', 'string'],
            'modalidad' => ['sometimes', 'required', 'in:Presencial,Virtual'],
            'lugar' => ['sometimes', 'nullable', 'string', 'max:255'],
            'enlace' => ['sometimes', 'nullable', 'string'],
            'frecuencia' => ['sometimes', 'nullable', 'string', 'max:100'],
        ]);

        if (array_key_exists('tipo_reunion', $data)) {
            $data['tipo_reunion'] = trim((string) $data['tipo_reunion']);
        }

        if (array_key_exists('hora', $data)) {
            $data['hora'] = $this->normalizeHora($data['hora']);
        }

        $reunion->fill($data)->save();

        HistorialLogger::log(
            $request->user()?->getKey(),
            'reunion_actualizada',
            'reuniones',
            (string) $reunion->getKey(),
            'Actualización de reunión',
            $antes,
            $reunion->toArray()
        );

        return response()->json($reunion);
    }

    public function destroy(Reunion $reunion)
    {
        $antes = $reunion->toArray();

        $reunion->delete();

        HistorialLogger::log(
            request()->user()?->getKey(),
            'reunion_eliminada',
            'reuniones',
            (string) ($antes['id_reunion'] ?? $reunion->getKey()),
            'Eliminación de reunión',
            $antes,
            null
        );

        return response()->json(null, 204);
    }

    public function syncRepresentantes(Request $request, Reunion $reunion)
    {
        $antes = $reunion->load('representantes')->representantes->map(function ($r) {
            return [
                'id_representante' => (int) $r->getKey(),
                'rol_participacion' => (string) ($r->pivot?->rol_participacion ?? 'Titular'),
                'asistio' => (string) ($r->pivot?->asistio ?? 'pendiente'),
            ];
        })->values()->all();

        $data = $request->validate([
            'representantes' => ['required', 'array', 'min:1'],
            'representantes.*.id_representante' => ['required', 'integer', 'exists:representantes,id_representante'],
            'representantes.*.rol_participacion' => ['nullable', 'in:Titular,Invitado'],
            'representantes.*.asistio' => ['nullable', 'in:si,no,pendiente'],
        ]);

        $payload = [];
        foreach ($data['representantes'] as $item) {
            $payload[$item['id_representante']] = [
                'rol_participacion' => $item['rol_participacion'] ?? 'Titular',
                'asistio' => $item['asistio'] ?? 'pendiente',
            ];
        }

        $reunion->representantes()->sync($payload);

        $despues = $reunion->load('representantes')->representantes->map(function ($r) {
            return [
                'id_representante' => (int) $r->getKey(),
                'rol_participacion' => (string) ($r->pivot?->rol_participacion ?? 'Titular'),
                'asistio' => (string) ($r->pivot?->asistio ?? 'pendiente'),
            ];
        })->values()->all();

        HistorialLogger::log(
            $request->user()?->getKey(),
            'reunion_representantes_sync',
            'reunion_representantes',
            (string) $reunion->getKey(),
            'Sincronización de representantes en reunión',
            ['representantes' => $antes],
            ['representantes' => $despues]
        );

        return response()->json($reunion->load('representantes'));
    }

    public function representantes(Reunion $reunion)
    {
        return response()->json($reunion->load('representantes')->representantes);
    }

    public function upsertRepresentante(Request $request, Reunion $reunion)
    {
        $data = $request->validate([
            'id_representante' => ['required', 'integer', 'exists:representantes,id_representante'],
            'rol_participacion' => ['nullable', 'in:Titular,Invitado'],
            'asistio' => ['nullable', 'in:si,no,pendiente'],
        ]);

        $idRepresentante = (int) $data['id_representante'];

        $antes = $reunion->representantes()
            ->where('representantes.id_representante', $idRepresentante)
            ->get()
            ->map(function ($r) {
                return [
                    'id_representante' => (int) $r->getKey(),
                    'rol_participacion' => (string) ($r->pivot?->rol_participacion ?? 'Titular'),
                    'asistio' => (string) ($r->pivot?->asistio ?? 'pendiente'),
                ];
            })
            ->values()
            ->all();

        $reunion->representantes()->syncWithoutDetaching([
            $idRepresentante => [
                'rol_participacion' => $data['rol_participacion'] ?? 'Titular',
                'asistio' => $data['asistio'] ?? 'pendiente',
            ],
        ]);

        $despues = $reunion->representantes()
            ->where('representantes.id_representante', $idRepresentante)
            ->get()
            ->map(function ($r) {
                return [
                    'id_representante' => (int) $r->getKey(),
                    'rol_participacion' => (string) ($r->pivot?->rol_participacion ?? 'Titular'),
                    'asistio' => (string) ($r->pivot?->asistio ?? 'pendiente'),
                ];
            })
            ->values()
            ->all();

        HistorialLogger::log(
            $request->user()?->getKey(),
            'reunion_representante_upsert',
            'reunion_representantes',
            (string) $reunion->getKey(),
            'Asignación/actualización de representante en reunión',
            ['representante' => $antes[0] ?? null],
            ['representante' => $despues[0] ?? null]
        );

        return response()->json($reunion->load('representantes'));
    }

    public function updateRepresentantePivot(Request $request, Reunion $reunion, Representante $representante)
    {
        $data = $request->validate([
            'rol_participacion' => ['sometimes', 'required', 'in:Titular,Invitado'],
            'asistio' => ['sometimes', 'required', 'in:si,no,pendiente'],
        ]);

        $antes = $reunion->representantes()
            ->where('representantes.id_representante', $representante->getKey())
            ->get()
            ->map(function ($r) {
                return [
                    'id_representante' => (int) $r->getKey(),
                    'rol_participacion' => (string) ($r->pivot?->rol_participacion ?? 'Titular'),
                    'asistio' => (string) ($r->pivot?->asistio ?? 'pendiente'),
                ];
            })
            ->values()
            ->all();

        if (count($antes) === 0) {
            return response()->json(['message' => 'El representante no está asignado a esta reunión.'], 422);
        }

        $reunion->representantes()->updateExistingPivot((int) $representante->getKey(), $data);

        $despues = $reunion->representantes()
            ->where('representantes.id_representante', $representante->getKey())
            ->get()
            ->map(function ($r) {
                return [
                    'id_representante' => (int) $r->getKey(),
                    'rol_participacion' => (string) ($r->pivot?->rol_participacion ?? 'Titular'),
                    'asistio' => (string) ($r->pivot?->asistio ?? 'pendiente'),
                ];
            })
            ->values()
            ->all();

        HistorialLogger::log(
            $request->user()?->getKey(),
            'reunion_representante_actualizado',
            'reunion_representantes',
            (string) $reunion->getKey(),
            'Actualización de participación de representante en reunión',
            ['representante' => $antes[0] ?? null],
            ['representante' => $despues[0] ?? null]
        );

        return response()->json($reunion->load('representantes'));
    }

    public function removeRepresentante(Request $request, Reunion $reunion, Representante $representante)
    {
        $antes = $reunion->representantes()
            ->where('representantes.id_representante', $representante->getKey())
            ->get()
            ->map(function ($r) {
                return [
                    'id_representante' => (int) $r->getKey(),
                    'rol_participacion' => (string) ($r->pivot?->rol_participacion ?? 'Titular'),
                    'asistio' => (string) ($r->pivot?->asistio ?? 'pendiente'),
                ];
            })
            ->values()
            ->all();

        $reunion->representantes()->detach((int) $representante->getKey());

        HistorialLogger::log(
            $request->user()?->getKey(),
            'reunion_representante_removido',
            'reunion_representantes',
            (string) $reunion->getKey(),
            'Eliminación de representante de reunión',
            ['representante' => $antes[0] ?? null],
            null
        );

        return response()->json($reunion->load('representantes'));
    }

    private function normalizeHora(mixed $value): ?string
    {
        if ($value === null) {
            return null;
        }

        $s = trim((string) $value);
        if ($s === '') {
            return null;
        }

        if (preg_match('/^\d{2}:\d{2}$/', $s) === 1) {
            return $s.':00';
        }

        if (preg_match('/^\d{2}:\d{2}:\d{2}$/', $s) === 1) {
            return $s;
        }

        throw ValidationException::withMessages([
            'hora' => 'Hora inválida. Usa formato HH:MM o HH:MM:SS.',
        ]);
    }
}
