<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\RequestDescargaProgramada;
use App\Models\Proveedor;
use App\Services\SATCfdiProgramadoService;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Validator;
use Exception;

class ProgrammedDownloads extends Controller
{
    /**
     * Crear una solicitud de descarga programada.
     *
     * POST /api/v1/programmed-downloads
     *
     * Body (JSON):
     *   id_proveedor  int     requerido  ID del proveedor en la base de datos
     *   anio          int     requerido  Año de la descarga (ej: 2025)
     *   mes           int     requerido  Mes de la descarga (1-12)
     *   tipo          string  requerido  'recibido' o 'emitido'
     *
     * @param Request $request
     * @return JsonResponse
     */
    public function crearSolicitud(Request $request): JsonResponse
    {
        $validator = Validator::make($request->all(), [
            'id_proveedor' => 'required|numeric|exists:Proveedor,id_proveedor',
            'anio'         => 'required|integer|min:2000|max:2100',
            'mes'          => 'required|integer|min:1|max:12',
            'tipo'         => 'required|in:recibido,emitido',
        ], [
            'id_proveedor.required' => 'El ID del proveedor es requerido.',
            'id_proveedor.exists'   => 'El proveedor no existe en la base de datos.',
            'anio.required'         => 'El año es requerido.',
            'anio.min'              => 'El año debe ser mayor o igual a 2000.',
            'anio.max'              => 'El año debe ser menor o igual a 2100.',
            'mes.required'          => 'El mes es requerido.',
            'mes.min'               => 'El mes debe estar entre 1 y 12.',
            'mes.max'               => 'El mes debe estar entre 1 y 12.',
            'tipo.required'         => 'El tipo de descarga es requerido.',
            'tipo.in'               => 'El tipo debe ser "recibido" o "emitido".',
        ]);

        if ($validator->fails()) {
            return response()->json([
                'success' => false,
                'message' => 'Error de validación',
                'errors'  => $validator->errors(),
            ], 422);
        }

        try {
            $idProveedor = $request->input('id_proveedor');
            $anio = $request->input('anio');
            $mes = $request->input('mes');
            $tipo = $request->input('tipo');

            // Verificar que el proveedor tenga certificado FIEL
            $proveedor = Proveedor::with('certificadoFiel')->find($idProveedor);

            if (!$proveedor->certificadoFiel) {
                return response()->json([
                    'success' => false,
                    'message' => 'El proveedor no tiene un certificado FIEL configurado.',
                ], 400);
            }

            // Verificar si ya existe una solicitud para este proveedor/periodo/tipo
            $existente = RequestDescargaProgramada::where([
                'id_proveedor' => $idProveedor,
                'anio'         => $anio,
                'mes'          => $mes,
                'tipo'         => $tipo,
            ])->whereIn('status', ['solicitado', 'en_proceso', 'descargando'])->first();

            if ($existente) {
                return response()->json([
                    'success' => false,
                    'message' => 'Ya existe una solicitud en proceso para este periodo.',
                    'data'    => [
                        'id'         => $existente->id,
                        'status'     => $existente->status,
                        'request_id' => $existente->request_id,
                    ],
                ], 409);
            }

            // Crear servicio con certificado del proveedor
            $service = new SATCfdiProgramadoService($idProveedor);

            // Crear solicitud al SAT
            $resultado = $service->crearSolicitudMensual($anio, $mes, $tipo);

            // Guardar en base de datos
            $solicitud = RequestDescargaProgramada::create([
                'id_proveedor'    => $idProveedor,
                'anio'            => $anio,
                'mes'             => $mes,
                'tipo'            => $tipo,
                'request_id'      => $resultado['request_id'],
                'status'          => 'solicitado',
                'fecha_solicitud' => now(),
                'retry_count'     => 0,
                'next_retry_at'   => now()->addMinutes(5), // Primer retry en 5 minutos
            ]);

            return response()->json([
                'success' => true,
                'message' => 'Solicitud de descarga creada exitosamente',
                'data'    => [
                    'id'             => $solicitud->id,
                    'id_proveedor'   => $solicitud->id_proveedor,
                    'anio'           => $solicitud->anio,
                    'mes'            => $solicitud->mes,
                    'tipo'           => $solicitud->tipo,
                    'request_id'     => $solicitud->request_id,
                    'status'         => $solicitud->status,
                    'fecha_solicitud' => $solicitud->fecha_solicitud,
                    'rfc'            => $resultado['rfc'],
                ],
            ], 201);

        } catch (Exception $e) {
            // Guardar error en base de datos si se pudo crear el registro
            if (isset($solicitud)) {
                $solicitud->update([
                    'status'        => 'error',
                    'error_message' => $e->getMessage(),
                ]);
            }

            return response()->json([
                'success' => false,
                'message' => 'Error al crear la solicitud de descarga',
                'error'   => $e->getMessage(),
            ], 500);
        }
    }

    /**
     * Verificar el estado de una solicitud y descargar si está lista.
     *
     * GET /api/v1/programmed-downloads/{id}/check
     *
     * @param mixed $id ID del registro en request_descarga_programada
     * @return JsonResponse
     */
    public function verificarSolicitud(mixed $id): JsonResponse
    {
        try {
            // Buscar la solicitud
            $solicitud = RequestDescargaProgramada::with('proveedor.certificadoFiel')->find($id);

            if (!$solicitud) {
                return response()->json([
                    'success' => false,
                    'message' => 'Solicitud no encontrada',
                ], 404);
            }

            // Validar que la solicitud esté en un estado verificable
            if (!in_array($solicitud->status, ['solicitado', 'en_proceso', 'listo', 'por_procesar'])) {
                return response()->json([
                    'success' => false,
                    'message' => "La solicitud ya está en estado '{$solicitud->status}' y no puede ser verificada",
                    'data'    => [
                        'id'     => $solicitud->id,
                        'status' => $solicitud->status,
                    ],
                ], 400);
            }

            // Verificar que tenga request_id
            if (!$solicitud->request_id) {
                return response()->json([
                    'success' => false,
                    'message' => 'La solicitud no tiene un request_id del SAT',
                ], 400);
            }

            // Crear servicio con certificado del proveedor
            $service = new SATCfdiProgramadoService($solicitud->id_proveedor);

            // Verificar estado y descargar si está listo
            $alreadyDownloaded = $solicitud->downloaded_packages ?? [];
            $resultado = $service->verificarYDescargar($solicitud->request_id, $alreadyDownloaded);

            // Actualizar registro en base de datos
            $updateData = [
                'status'        => $resultado['status'],
                'numero_cfdis'  => $resultado['numero_cfdis'] ?? 0,
            ];

            if (isset($resultado['package_ids'])) {
                $updateData['package_ids'] = $resultado['package_ids'];
            }

            if ($resultado['status'] === 'por_procesar') {
                $updateData['fecha_completado'] = now();
            }

            $solicitud->update($updateData);

            return response()->json([
                'success' => true,
                'message' => $this->getStatusMessage($resultado['status'], $resultado),
                'data'    => [
                    'id'                 => $solicitud->id,
                    'id_proveedor'       => $solicitud->id_proveedor,
                    'anio'               => $solicitud->anio,
                    'mes'                => $solicitud->mes,
                    'tipo'               => $solicitud->tipo,
                    'status'             => $resultado['status'],
                    'code_request'       => $resultado['code_request'] ?? null,
                    'status_message'     => $resultado['status_message'] ?? null,
                    'numero_cfdis'       => $resultado['numero_cfdis'] ?? 0,
                    'archivos_extraidos' => $resultado['archivos_extraidos'] ?? 0,
                    'ruta_xml'           => $resultado['ruta_xml'] ?? null,
                    'fecha_completado'   => $solicitud->fecha_completado,
                ],
            ], 200);

        } catch (Exception $e) {
            // Actualizar estado a error en la base de datos
            if (isset($solicitud)) {
                $solicitud->update([
                    'status'        => 'error',
                    'error_message' => $e->getMessage(),
                ]);
            }

            return response()->json([
                'success' => false,
                'message' => 'Error al verificar la solicitud',
                'error'   => $e->getMessage(),
            ], 500);
        }
    }

    /**
     * Obtener mensaje descriptivo según el estado.
     *
     * @param string $status
     * @param array $resultado
     * @return string
     */
    private function getStatusMessage(string $status, array $resultado): string
    {
        switch ($status) {
            case 'por_procesar':
                $archivos = $resultado['archivos_extraidos'] ?? 0;
                if ($archivos > 0) {
                    return "Descarga completada. Se extrajeron {$archivos} archivos XML.";
                }
                return "Solicitud completada. No se encontraron CFDIs en el periodo solicitado.";
            
            case 'en_proceso':
                return "La solicitud está en proceso. El SAT aún está preparando los paquetes.";
            
            case 'listo':
                return "Los paquetes están listos para descargar.";
            
            case 'procesado':
                return "Los XMLs han sido procesados por el sistema externo.";
            
            default:
                return "Estado: {$status}";
        }
    }
}
