<?php

namespace App\Http\Controllers;

use App\Models\EstadoOrden;
use App\Models\Frecuencia;
use App\Models\OrdenItem;
use App\Models\OrdenLog;
use App\Models\OrdenTrabajo;
use App\Models\ParoPlanta;
use App\Models\PlanMantenimiento;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;

class ProgramacionController extends Controller
{
    public function index(Request $r)
    {
        $mes = $r->integer('mes') ?: now()->month;
        $anio = $r->integer('anio') ?: now()->year;
        $inicio = Carbon::create($anio, $mes, 1)->startOfDay();
        $fin = $inicio->copy()->endOfMonth();

        $ordenes = OrdenTrabajo::with(['equipo','estado','responsable','frecuencia'])
            ->whereBetween('fecha_programada', [$inicio, $fin])
            ->orderBy('fecha_programada')->get()
            ->groupBy(fn ($o) => $o->fecha_programada?->format('Y-m-d'));

        $planes = PlanMantenimiento::where('activo', 1)->where('clase','preventivo')
            ->orderBy('codigo')->get();

        $parosActivos = ParoPlanta::with('usuario')
            ->whereNull('fecha_fin')->orderBy('fecha_inicio')->get();
        $parosHistorial = ParoPlanta::with('usuario')
            ->whereNotNull('fecha_fin')->orderByDesc('fecha_fin')->limit(10)->get();

        return view('ordenes.programacion', compact(
            'ordenes','inicio','fin','mes','anio','planes','parosActivos','parosHistorial'
        ));
    }

    /**
     * Genera OT preventivas para uno o varios planes en un rango de fechas.
     * Una OT por (plan, equipo, frecuencia, fecha) agrupa todos los ítems del plan
     * con esa frecuencia en una única visita.
     */
    public function generar(Request $r)
    {
        $r->validate([
            'desde' => 'required|date',
            'hasta' => 'required|date|after_or_equal:desde',
            'plan_ids' => 'nullable',
            'plan_ids.*' => 'integer|exists:planes_mantenimiento,id',
        ]);

        $desde = Carbon::parse($r->desde)->startOfDay();
        $hasta = Carbon::parse($r->hasta)->endOfDay();
        $estadoPen = EstadoOrden::where('codigo','PEN')->first();

        // Planes a procesar
        $planesQ = PlanMantenimiento::with(['items.frecuencia'])
            ->where('activo', 1)->where('clase','preventivo');
        $ids = $r->input('plan_ids');
        if (is_array($ids) && count($ids)) {
            $planesQ->whereIn('id', $ids);
        }
        $planes = $planesQ->get();

        if ($planes->isEmpty()) {
            return back()->with('error','No hay planes activos para generar.');
        }

        $totalGeneradas = 0;
        $totalOmitidas = 0;

        // Mapear flags pm_* a frecuencias del catálogo (por horas_pm)
        $frecuenciasPorPm = [
            'pm_360' => Frecuencia::where('horas_pm', 360)->first(),
            'pm_720' => Frecuencia::where('horas_pm', 720)->first(),
            'pm_4320' => Frecuencia::where('horas_pm', 4320)->first(),
            'pm_8640' => Frecuencia::where('horas_pm', 8640)->first(),
        ];

        DB::transaction(function () use ($planes, $desde, $hasta, $estadoPen, $frecuenciasPorPm, &$totalGeneradas, &$totalOmitidas) {
            foreach ($planes as $plan) {
                if (!$plan->equipo_id) continue;

                // Agrupar items por flag pm_* (un item puede aplicar a varias frecuencias)
                foreach ($frecuenciasPorPm as $flag => $frecuencia) {
                    if (!$frecuencia || !$frecuencia->dias) continue;
                    $items = $plan->items->filter(fn ($i) => $i->$flag);
                    if ($items->isEmpty()) continue;

                    $dias = (int) $frecuencia->dias;
                    $fecha = $desde->copy();
                    while ($fecha->lte($hasta)) {
                        $existe = OrdenTrabajo::where('plan_id', $plan->id)
                            ->where('equipo_id', $plan->equipo_id)
                            ->where('frecuencia_id', $frecuencia->id)
                            ->whereDate('fecha_programada', $fecha->toDateString())
                            ->exists();

                        if ($existe) {
                            $totalOmitidas++;
                        } else {
                            $orden = OrdenTrabajo::create([
                                'codigo' => $this->generarCodigo('preventivo'),
                                'clase' => 'preventivo',
                                'plan_id' => $plan->id,
                                'frecuencia_id' => $frecuencia->id,
                                'area_id' => $plan->area_id,
                                'sistema_id' => $plan->sistema_id,
                                'equipo_id' => $plan->equipo_id,
                                'estado_id' => $estadoPen?->id,
                                'fecha_programada' => $fecha->toDateString(),
                                'descripcion' => "Generada automáticamente · {$frecuencia->nombre} ({$dias}d) · Plan {$plan->codigo}",
                                'creado_por' => auth()->id(),
                            ]);

                            foreach ($items as $it) {
                                OrdenItem::create([
                                    'orden_id' => $orden->id,
                                    'plan_item_id' => $it->id,
                                    'numero_item' => $it->numero_item,
                                    'descripcion' => $it->descripcion,
                                    'estado_item' => 'pendiente',
                                    'lila_l_limpieza' => $it->lila_l_limpieza,
                                    'lila_i_inspeccion' => $it->lila_i_inspeccion,
                                    'lila_l_lubricacion' => $it->lila_l_lubricacion,
                                    'lila_a_ajuste' => $it->lila_a_ajuste,
                                ]);
                            }
                            $totalGeneradas++;
                        }
                        $fecha->addDays($dias);
                    }
                }
            }
        });

        $msg = "Se generaron {$totalGeneradas} órdenes preventivas.";
        if ($totalOmitidas) $msg .= " {$totalOmitidas} omitidas (ya existían).";
        return back()->with('exito', $msg);
    }

    /**
     * Replanifica OT pendientes desplazando N días desde una fecha pivote.
     * Útil cuando hay un adelanto/atraso no registrado formalmente como paro.
     */
    public function replanificar(Request $r)
    {
        $r->validate([
            'desde' => 'required|date',
            'dias' => 'required|integer',
            'motivo' => 'required|string|max:200',
        ]);
        $desde = Carbon::parse($r->desde);
        $dias = (int) $r->dias;
        $motivo = $r->input('motivo');

        if ($dias === 0) return back()->with('error','Indica un número de días distinto de cero.');

        $estadoREP = EstadoOrden::where('codigo','REP')->value('id');

        $ots = OrdenTrabajo::whereHas('estado', fn ($e) => $e->whereIn('codigo', ['PEN','REP']))
            ->whereDate('fecha_programada', '>=', $desde)
            ->get();

        DB::transaction(function () use ($ots, $dias, $motivo, $estadoREP) {
            foreach ($ots as $ot) {
                $fechaOriginal = $ot->fecha_programada;
                $nueva = $fechaOriginal->copy()->addDays($dias);
                $ot->fecha_programada = $nueva;
                $ot->estado_id = $estadoREP;
                $ot->save();

                OrdenLog::create([
                    'orden_id' => $ot->id,
                    'usuario_id' => auth()->id(),
                    'accion' => 'replanificada_manual',
                    'detalle' => "Desplazada {$dias} días — {$motivo}. Original: {$fechaOriginal->format('d/m/Y')}",
                ]);
            }
        });

        return back()->with('exito', count($ots) . " órdenes replanificadas ({$dias} días).");
    }

    private function generarCodigo(string $clase): string
    {
        $prefijo = $clase === 'correctivo'
            ? \App\Models\Configuracion::obtener('prefijo_orden_correctiva', 'OTC')
            : \App\Models\Configuracion::obtener('prefijo_orden_preventiva', 'OTP');
        $anio = now()->year;
        $pat = $prefijo . '-' . $anio . '-';
        // Tomar el mayor sufijo numérico existente y sumarle 1 (no usar count)
        $ultimo = OrdenTrabajo::withTrashed()
            ->where('codigo', 'like', $pat . '%')
            ->selectRaw("MAX(CAST(SUBSTRING(codigo, ?) AS UNSIGNED)) as ultimo", [strlen($pat) + 1])
            ->value('ultimo');
        $siguiente = ((int) $ultimo) + 1;
        return $pat . str_pad((string) $siguiente, 5, '0', STR_PAD_LEFT);
    }
}
