<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\DocumentoRepresentante;
use App\Support\HistorialLogger;
use Illuminate\Http\Request;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Str;
use OpenSpout\Common\Entity\Row;
use OpenSpout\Common\Entity\Style\Color;
use OpenSpout\Common\Entity\Style\Style;
use OpenSpout\Writer\XLSX\Writer as XLSXWriter;

class DocumentoRepresentanteController extends Controller
{
    public function index(Request $request)
    {
        $query = DocumentoRepresentante::query()->with('representante');

        if ($request->filled('id_representante')) {
            $query->where('id_representante', $request->integer('id_representante'));
        }

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

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

        $paginator = $query->orderByDesc('fecha_subida')->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 = DocumentoRepresentante::query()->with('representante');

        if ($request->filled('id_representante')) {
            $query->where('id_representante', $request->integer('id_representante'));
        }

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

        $fileName = 'documentos_representante.xlsx';

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

            $headerStyle = (new Style())->setFontBold();
            $writer->addRow(Row::fromValues([
                'ID',
                'Representante ID',
                'Representante',
                'Tipo',
                'Archivo',
                'Tipo de archivo',
                'Ruta',
                'Hash SHA256',
                'Fecha de subida',
            ], $headerStyle));

            $rowStyleEven = (new Style())->setBackgroundColor(Color::WHITE);
            $rowStyleOdd = (new Style())->setBackgroundColor(Color::rgb(243, 251, 255));
            $rowIndex = 0;

            $query->orderByDesc('fecha_subida')->chunk(500, function ($rows) use ($writer, $rowStyleEven, $rowStyleOdd, &$rowIndex) {
                foreach ($rows as $row) {
                    $rowIndex++;
                    $style = $rowIndex % 2 === 0 ? $rowStyleEven : $rowStyleOdd;
                    $repNombre = '';
                    if ($row->representante) {
                        $repNombre = trim((string) (($row->representante->nombres ?? '').' '.($row->representante->apellidos ?? '')));
                    }

                    $writer->addRow(Row::fromValues([
                        (string) ($row->id_documento ?? ''),
                        (string) ($row->id_representante ?? ''),
                        $repNombre,
                        (string) ($row->tipo_documento ?? ''),
                        (string) ($row->nombre_archivo ?? ''),
                        (string) ($row->tipo_archivo ?? ''),
                        (string) ($row->ruta_archivo ?? ''),
                        (string) ($row->hash_sha256 ?? ''),
                        (string) ($row->fecha_subida ?? ''),
                    ], $style));
                }
            });

            $sheet->setColumnWidth(12, 1);
            $sheet->setColumnWidth(18, 2);
            $sheet->setColumnWidth(28, 3);
            $sheet->setColumnWidth(12, 4);
            $sheet->setColumnWidth(34, 5);
            $sheet->setColumnWidth(20, 6);
            $sheet->setColumnWidth(34, 7);
            $sheet->setColumnWidth(64, 8);
            $sheet->setColumnWidth(18, 9);

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

    public function upload(Request $request)
    {
        $data = $request->validate([
            'id_representante' => ['required', 'integer', 'exists:representantes,id_representante'],
            'tipo_documento' => ['required', 'in:CV,Resolucion'],
            'archivo' => ['required', 'file', 'max:10240', 'mimes:pdf,doc,docx'],
            'nombre_archivo' => ['sometimes', 'nullable', 'string', 'max:255'],
        ]);

        /** @var UploadedFile $file */
        $file = $request->file('archivo');
        $idRepresentante = (int) $data['id_representante'];
        $tipoDocumento = (string) $data['tipo_documento'];

        $originalName = (string) $file->getClientOriginalName();
        $displayName = array_key_exists('nombre_archivo', $data) && is_string($data['nombre_archivo']) && trim($data['nombre_archivo']) !== ''
            ? trim($data['nombre_archivo'])
            : $originalName;

        $ext = strtolower((string) $file->getClientOriginalExtension());
        if ($ext === '') {
            $ext = 'bin';
        }

        $storedName = (string) Str::uuid().'.'.$ext;
        $relativeDir = 'private/documentos_representante/'.$idRepresentante;
        $relativePath = $relativeDir.'/'.$storedName;
        $absoluteDir = storage_path('app'.DIRECTORY_SEPARATOR.$relativeDir);

        if (! is_dir($absoluteDir)) {
            mkdir($absoluteDir, 0755, true);
        }

        $absolutePath = $absoluteDir.DIRECTORY_SEPARATOR.$storedName;
        $file->move($absoluteDir, $storedName);

        $hash = null;
        try {
            $hash = hash_file('sha256', $absolutePath) ?: null;
        } catch (\Throwable) {
            $hash = null;
        }

        $mime = (string) ($file->getClientMimeType() ?: $file->getMimeType() ?: '');
        if ($mime === '') {
            $mime = 'application/octet-stream';
        }

        $documento = DocumentoRepresentante::create([
            'id_representante' => $idRepresentante,
            'tipo_documento' => $tipoDocumento,
            'nombre_archivo' => $displayName,
            'tipo_archivo' => $mime,
            'ruta_archivo' => $relativePath,
            'hash_sha256' => $hash,
            'fecha_subida' => now(),
        ]);

        HistorialLogger::log(
            $request->user()?->getKey(),
            'documento_representante_subido',
            'documentos_representante',
            (string) $documento->getKey(),
            'Subida de documento de representante',
            null,
            $documento->toArray()
        );

        return response()->json($documento->load('representante'), 201);
    }

    public function store(Request $request)
    {
        $data = $request->validate([
            'id_representante' => ['required', 'integer', 'exists:representantes,id_representante'],
            'tipo_documento' => ['required', 'in:CV,Resolucion'],
            'nombre_archivo' => ['required', 'string', 'max:255'],
            'tipo_archivo' => ['required', 'string', 'max:100'],
            'ruta_archivo' => ['required', 'string', 'max:500'],
            'hash_sha256' => ['nullable', 'string', 'size:64'],
        ]);

        $ruta = (string) $data['ruta_archivo'];
        $idRepresentante = (int) $data['id_representante'];
        $normalized = ltrim($ruta, '/');
        if (str_contains($normalized, '..')) {
            return response()->json(['message' => 'Ruta de archivo inválida.'], 422);
        }

        $allowedPrefixes = [
            'private/documentos_representante/'.$idRepresentante.'/',
            'uploads/representantes/'.$idRepresentante.'/',
        ];
        $allowed = false;
        foreach ($allowedPrefixes as $prefix) {
            if (str_starts_with($normalized, $prefix)) {
                $allowed = true;
                break;
            }
        }
        if (! $allowed) {
            return response()->json(['message' => 'Ruta de archivo inválida.'], 422);
        }

        $absolutePath = $this->resolveAbsoluteFilePath($ruta);
        if (! is_string($absolutePath) || $absolutePath === '' || ! is_file($absolutePath)) {
            return response()->json(['message' => 'Archivo no encontrado.'], 422);
        }

        $documento = DocumentoRepresentante::create($data);

        HistorialLogger::log(
            $request->user()?->getKey(),
            'documento_representante_creado',
            'documentos_representante',
            (string) $documento->getKey(),
            'Registro de documento de representante',
            null,
            $documento->toArray()
        );

        return response()->json($documento->load('representante'), 201);
    }

    public function show(DocumentoRepresentante $documentoRepresentante)
    {
        return response()->json($documentoRepresentante->load('representante'));
    }

    public function download(Request $request, DocumentoRepresentante $documentoRepresentante)
    {
        $ruta = (string) ($documentoRepresentante->ruta_archivo ?? '');
        $absolutePath = $this->resolveAbsoluteFilePath($ruta);
        if (! is_string($absolutePath) || $absolutePath === '' || ! is_file($absolutePath)) {
            return response()->json(['message' => 'Archivo no encontrado.'], 404);
        }

        $fileName = trim((string) ($documentoRepresentante->nombre_archivo ?? 'documento'));
        if ($fileName === '') {
            $fileName = 'documento';
        }
        $fileName = str_replace(["\r", "\n"], ' ', $fileName);

        return response()->download($absolutePath, $fileName, [
            'X-Content-Type-Options' => 'nosniff',
        ]);
    }

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

        $data = $request->validate([
            'id_representante' => ['sometimes', 'required', 'integer', 'exists:representantes,id_representante'],
            'tipo_documento' => ['sometimes', 'required', 'in:CV,Resolucion'],
            'nombre_archivo' => ['sometimes', 'required', 'string', 'max:255'],
            'tipo_archivo' => ['sometimes', 'required', 'string', 'max:100'],
            'hash_sha256' => ['sometimes', 'nullable', 'string', 'size:64'],
        ]);

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

        $despues = $documentoRepresentante->toArray();
        HistorialLogger::log(
            $request->user()?->getKey(),
            'documento_representante_actualizado',
            'documentos_representante',
            (string) $documentoRepresentante->getKey(),
            'Actualización de documento de representante',
            $antes,
            $despues
        );

        return response()->json($documentoRepresentante->load('representante'));
    }

    public function destroy(Request $request, DocumentoRepresentante $documentoRepresentante)
    {
        $antes = $documentoRepresentante->toArray();

        $ruta = (string) $documentoRepresentante->ruta_archivo;
        $absolutePath = $this->resolveAbsoluteFilePath($ruta);
        if (is_string($absolutePath) && $absolutePath !== '' && is_file($absolutePath)) {
            try {
                @unlink($absolutePath);
            } catch (\Throwable) {
            }
        }

        $documentoRepresentante->delete();

        HistorialLogger::log(
            $request->user()?->getKey(),
            'documento_representante_eliminado',
            'documentos_representante',
            (string) $antes['id_documento'],
            'Eliminación de documento de representante',
            $antes,
            null
        );

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

    private function resolveAbsoluteFilePath(string $ruta): ?string
    {
        $ruta = trim($ruta);
        if ($ruta === '') {
            return null;
        }

        if (str_starts_with($ruta, '/uploads/')) {
            $candidate = public_path(ltrim($ruta, '/'));
            $realCandidate = realpath($candidate);
            $realBase = realpath(public_path('uploads'));
            if (is_string($realCandidate) && is_string($realBase) && str_starts_with($realCandidate, $realBase)) {
                return $realCandidate;
            }

            return null;
        }

        $normalized = ltrim($ruta, '/');
        $candidate = storage_path('app'.DIRECTORY_SEPARATOR.$normalized);
        $realCandidate = realpath($candidate);
        $realBase = realpath(storage_path('app'.DIRECTORY_SEPARATOR.'private'));
        if (is_string($realCandidate) && is_string($realBase) && str_starts_with($realCandidate, $realBase)) {
            return $realCandidate;
        }

        return null;
    }
}
