<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Alerta;
use App\Models\Institucion;
use App\Models\InstitucionPadre;
use App\Models\Representante;
use App\Models\Reunion;
use App\Models\Sector;
use App\Models\Usuario;
use App\Support\Rbac;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Schema;
use Illuminate\Validation\ValidationException;
use OpenSpout\Common\Entity\Row;
use OpenSpout\Common\Entity\Style\Style;
use OpenSpout\Writer\XLSX\Writer as XLSXWriter;

class ExportacionController extends Controller
{
    public function config(Request $request)
    {
        $user = $request->user();
        $modules = [];

        foreach ($this->definitions() as $def) {
            if (! $user || ! Rbac::puede($user, $def['key'], 'ver')) {
                continue;
            }

            $modules[] = [
                'key' => $def['key'],
                'label' => $def['label'],
                'columns' => array_values(array_map(fn ($c) => ['key' => $c['key'], 'label' => $c['label']], $def['columns'])),
            ];
        }

        return response()->json(['modules' => $modules]);
    }

    public function export(Request $request)
    {
        $user = $request->user();
        if (! $user) {
            return response()->json(['message' => 'No autorizado.'], 401);
        }

        $payload = $request->validate([
            'modules' => ['required', 'array', 'min:1'],
            'modules.*' => ['required', 'string'],
            'columns' => ['required', 'array'],
        ]);

        $requestedModules = array_values(array_unique(array_map('strval', $payload['modules'] ?? [])));
        $requestedColumns = is_array($payload['columns'] ?? null) ? $payload['columns'] : [];

        $defsByKey = [];
        foreach ($this->definitions() as $d) {
            $defsByKey[$d['key']] = $d;
        }

        $selected = [];
        foreach ($requestedModules as $key) {
            $def = $defsByKey[$key] ?? null;
            if (! $def) {
                throw ValidationException::withMessages(['modules' => "Módulo inválido: {$key}"]);
            }
            if (! Rbac::puede($user, $key, 'ver')) {
                return response()->json(['message' => 'Sin permisos para exportar uno de los módulos seleccionados.'], 403);
            }

            $cols = $requestedColumns[$key] ?? null;
            if (! is_array($cols) || count($cols) === 0) {
                throw ValidationException::withMessages(['columns' => "Selecciona al menos una columna para: {$def['label']}"]);
            }

            $cols = array_values(array_unique(array_map('strval', $cols)));
            $allowedCols = array_column($def['columns'], null, 'key');
            $finalCols = [];
            foreach ($cols as $colKey) {
                if (! isset($allowedCols[$colKey])) {
                    throw ValidationException::withMessages(['columns' => "Columna inválida ({$def['label']}): {$colKey}"]);
                }
                $finalCols[] = $allowedCols[$colKey];
            }

            $selected[] = ['def' => $def, 'columns' => $finalCols];
        }

        $fileName = 'exportacion.xlsx';

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

            $headerStyle = (new Style())->setFontBold();
            $usedSheetNames = [];
            $sheetIndex = 0;

            foreach ($selected as $item) {
                $def = $item['def'];
                $cols = $item['columns'];

                if ($sheetIndex > 0) {
                    $writer->addNewSheetAndMakeItCurrent();
                }

                $sheetName = $this->uniqueSheetName($def['label'], $usedSheetNames);
                $writer->getCurrentSheet()->setName($sheetName);

                $writer->addRow(Row::fromValues(array_map(fn ($c) => $c['label'], $cols), $headerStyle));

                /** @var callable():Builder $queryFactory */
                $queryFactory = $def['query'];
                $query = $queryFactory();

                $query->chunk(500, function ($rows) use ($writer, $cols) {
                    foreach ($rows as $row) {
                        $values = [];
                        foreach ($cols as $c) {
                            $fn = $c['value'];
                            $v = $fn($row);
                            if (is_bool($v)) {
                                $values[] = $v ? '1' : '0';
                            } elseif ($v === null) {
                                $values[] = '';
                            } else {
                                $values[] = (string) $v;
                            }
                        }
                        $writer->addRow(Row::fromValues($values));
                    }
                });

                $sheetIndex++;
            }

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

    private function uniqueSheetName(string $label, array &$used): string
    {
        $base = trim($label) !== '' ? trim($label) : 'Hoja';
        $base = mb_substr($base, 0, 31);

        if (! isset($used[$base])) {
            $used[$base] = 1;
            return $base;
        }

        $i = $used[$base] + 1;
        $used[$base] = $i;

        $suffix = ' '.$i;
        $maxBase = max(1, 31 - mb_strlen($suffix));
        $name = mb_substr($base, 0, $maxBase).$suffix;

        return $name;
    }

    private function definitions(): array
    {
        $hasInstitucionPadre = Schema::hasTable('instituciones_padre') && Schema::hasColumn('instituciones', 'id_institucion_padre');

        return [
            [
                'key' => 'sectores',
                'label' => 'Sectores',
                'query' => fn () => Sector::query()->withCount('instituciones')->orderBy('nombre_sector'),
                'columns' => [
                    ['key' => 'nombre_sector', 'label' => 'Sector', 'value' => fn ($r) => $r->nombre_sector ?? ''],
                    ['key' => 'estado', 'label' => 'Estado', 'value' => fn ($r) => $r->estado ?? ''],
                    ['key' => 'organismos', 'label' => 'Organismos', 'value' => fn ($r) => $r->instituciones_count ?? 0],
                ],
            ],
            [
                'key' => 'instituciones-padre',
                'label' => 'Instituciones',
                'query' => fn () => InstitucionPadre::query()->withCount('organismos')->orderBy('nombre'),
                'columns' => [
                    ['key' => 'nombre', 'label' => 'Institución', 'value' => fn ($r) => $r->nombre ?? ''],
                    ['key' => 'descripcion', 'label' => 'Descripción', 'value' => fn ($r) => $r->descripcion ?? ''],
                    ['key' => 'estado', 'label' => 'Estado', 'value' => fn ($r) => $r->estado ?? ''],
                    ['key' => 'organismos', 'label' => 'Organismos', 'value' => fn ($r) => $r->organismos_count ?? 0],
                ],
            ],
            [
                'key' => 'instituciones',
                'label' => 'Organismos',
                'query' => function () use ($hasInstitucionPadre) {
                    $select = [
                        'id_institucion',
                        'nombre',
                        'descripcion',
                        'estado',
                        'id_sector',
                        'direccion',
                        'ruc',
                    ];
                    if ($hasInstitucionPadre) {
                        $select[] = 'id_institucion_padre';
                    }

                    $with = ['sector:id_sector,nombre_sector'];
                    if ($hasInstitucionPadre) {
                        $with[] = 'institucionPadre:id_institucion_padre,nombre';
                    }

                    return Institucion::query()
                        ->select($select)
                        ->with($with)
                        ->withCount(['representantes', 'documentos'])
                        ->orderBy('nombre');
                },
                'columns' => array_values(array_filter([
                    ['key' => 'nombre', 'label' => 'Organismo', 'value' => fn ($r) => $r->nombre ?? ''],
                    ['key' => 'sector', 'label' => 'Sector', 'value' => fn ($r) => $r->sector?->nombre_sector ?? ''],
                    $hasInstitucionPadre ? ['key' => 'institucion', 'label' => 'Institución', 'value' => fn ($r) => $r->institucionPadre?->nombre ?? ''] : null,
                    ['key' => 'descripcion', 'label' => 'Descripción', 'value' => fn ($r) => $r->descripcion ?? ''],
                    ['key' => 'estado', 'label' => 'Estado', 'value' => fn ($r) => $r->estado ?? ''],
                    ['key' => 'direccion', 'label' => 'Dirección', 'value' => fn ($r) => $r->direccion ?? ''],
                    ['key' => 'ruc', 'label' => 'RUC', 'value' => fn ($r) => $r->ruc ?? ''],
                    ['key' => 'representantes', 'label' => 'Representantes', 'value' => fn ($r) => $r->representantes_count ?? 0],
                    ['key' => 'documentos', 'label' => 'Documentos', 'value' => fn ($r) => $r->documentos_count ?? 0],
                ])),
            ],
            [
                'key' => 'representantes',
                'label' => 'Representantes',
                'query' => fn () => Representante::query()
                    ->select([
                        'id_representante',
                        'nombres',
                        'apellidos',
                        'dni',
                        'universidad',
                        'celular',
                        'correo',
                        'fecha_inicio',
                        'fecha_fin',
                        'estado',
                        'id_institucion',
                    ])
                    ->with([
                        'institucion:id_institucion,nombre,id_sector',
                        'institucion.sector:id_sector,nombre_sector',
                    ])
                    ->orderBy('apellidos')
                    ->orderBy('nombres'),
                'columns' => [
                    ['key' => 'organismo', 'label' => 'Organismo', 'value' => fn ($r) => $r->institucion?->nombre ?? ''],
                    ['key' => 'sector', 'label' => 'Sector', 'value' => fn ($r) => $r->institucion?->sector?->nombre_sector ?? ''],
                    ['key' => 'nombres', 'label' => 'Nombres', 'value' => fn ($r) => $r->nombres ?? ''],
                    ['key' => 'apellidos', 'label' => 'Apellidos', 'value' => fn ($r) => $r->apellidos ?? ''],
                    ['key' => 'dni', 'label' => 'DNI', 'value' => fn ($r) => $r->dni ?? ''],
                    ['key' => 'celular', 'label' => 'Celular', 'value' => fn ($r) => $r->celular ?? ''],
                    ['key' => 'correo', 'label' => 'Correo', 'value' => fn ($r) => $r->correo ?? ''],
                    ['key' => 'inicio', 'label' => 'Inicio', 'value' => fn ($r) => $r->fecha_inicio ?? ''],
                    ['key' => 'fin', 'label' => 'Fin', 'value' => fn ($r) => $r->fecha_fin ?? ''],
                    ['key' => 'estado', 'label' => 'Estado', 'value' => fn ($r) => $r->estado ?? ''],
                ],
            ],
            [
                'key' => 'reuniones',
                'label' => 'Reuniones',
                'query' => fn () => Reunion::query()->withCount(['representantes', 'actas'])->orderByDesc('fecha'),
                'columns' => [
                    ['key' => 'tipo_reunion', 'label' => 'Tipo', 'value' => fn ($r) => $r->tipo_reunion ?? ''],
                    ['key' => 'fecha', 'label' => 'Fecha', 'value' => fn ($r) => $r->fecha ?? ''],
                    ['key' => 'hora', 'label' => 'Hora', 'value' => fn ($r) => $r->hora ?? ''],
                    ['key' => 'modalidad', 'label' => 'Modalidad', 'value' => fn ($r) => $r->modalidad ?? ''],
                    ['key' => 'lugar', 'label' => 'Lugar', 'value' => fn ($r) => $r->lugar ?? ''],
                    ['key' => 'enlace', 'label' => 'Enlace', 'value' => fn ($r) => $r->enlace ?? ''],
                    ['key' => 'frecuencia', 'label' => 'Frecuencia', 'value' => fn ($r) => $r->frecuencia ?? ''],
                    ['key' => 'representantes', 'label' => 'Representantes', 'value' => fn ($r) => $r->representantes_count ?? 0],
                    ['key' => 'actas', 'label' => 'Actas', 'value' => fn ($r) => $r->actas_count ?? 0],
                ],
            ],
            [
                'key' => 'usuarios',
                'label' => 'Usuarios',
                'query' => fn () => Usuario::query()->select([
                    'nombres',
                    'apellidos',
                    'correo',
                    'rol',
                    'estado',
                    'email_verificado_en',
                    'ultimo_acceso',
                ])->orderBy('nombres')->orderBy('apellidos'),
                'columns' => [
                    ['key' => 'nombres', 'label' => 'Nombres', 'value' => fn ($r) => $r->nombres ?? ''],
                    ['key' => 'apellidos', 'label' => 'Apellidos', 'value' => fn ($r) => $r->apellidos ?? ''],
                    ['key' => 'correo', 'label' => 'Correo', 'value' => fn ($r) => $r->correo ?? ''],
                    ['key' => 'rol', 'label' => 'Rol', 'value' => fn ($r) => $r->rol ?? ''],
                    ['key' => 'estado', 'label' => 'Estado', 'value' => fn ($r) => $r->estado ?? ''],
                    ['key' => 'email_verificado_en', 'label' => 'Email verificado', 'value' => fn ($r) => $r->email_verificado_en ?? ''],
                    ['key' => 'ultimo_acceso', 'label' => 'Último acceso', 'value' => fn ($r) => $r->ultimo_acceso ?? ''],
                ],
            ],
            [
                'key' => 'alertas',
                'label' => 'Alertas',
                'query' => fn () => Alerta::query()
                    ->with([
                        'representante:id_representante,nombres,apellidos,dni,id_institucion',
                        'representante.institucion:id_institucion,nombre',
                    ])
                    ->orderByDesc('fecha_generada'),
                'columns' => [
                    ['key' => 'estado', 'label' => 'Estado', 'value' => fn ($r) => $r->estado ?? ''],
                    ['key' => 'tipo_alerta', 'label' => 'Tipo', 'value' => fn ($r) => $r->tipo_alerta ?? ''],
                    ['key' => 'fecha_objetivo', 'label' => 'Fecha objetivo', 'value' => fn ($r) => $r->fecha_objetivo ?? ''],
                    ['key' => 'fecha_generada', 'label' => 'Generada', 'value' => fn ($r) => $r->fecha_generada ?? ''],
                    ['key' => 'fecha_leida', 'label' => 'Leída', 'value' => fn ($r) => $r->fecha_leida ?? ''],
                    ['key' => 'representante', 'label' => 'Representante', 'value' => fn ($r) => trim((string) (($r->representante?->nombres ?? '').' '.($r->representante?->apellidos ?? '')))],
                    ['key' => 'dni', 'label' => 'DNI', 'value' => fn ($r) => $r->representante?->dni ?? ''],
                    ['key' => 'organismo', 'label' => 'Organismo', 'value' => $hasInstitucionPadre ? fn ($r) => $r->representante?->institucion?->nombre ?? '' : fn ($r) => $r->representante?->institucion?->nombre ?? ''],
                ],
            ],
        ];
    }
}

