<?php

namespace App\Imports;

use App\Models\Area;
use App\Models\Componente;
use App\Models\Equipo;
use App\Models\Frecuencia;
use App\Models\Importacion;
use App\Models\ImportacionError;
use App\Models\PlanItem;
use App\Models\PlanMantenimiento;
use App\Models\TipoMantenimiento;
use App\Models\Unidad;
use Illuminate\Support\Str;
use Maatwebsite\Excel\Facades\Excel as ExcelFacade;

/**
 * Importador del LIBRO.xlsx
 *
 * Formato esperado por hoja:
 *   Fila 1 (cabecera): AREA | EQUIPO | COMPONENTE | ITEM | DESCRIPCION | FRECUENCIA LUBRICACION |
 *                      CODIGO | CANTIDAD | UNIDAD | ANUAL | MENSUAL | QUINCENAL | SEMESTRAL |
 *                      PM 01 ... 360h | PM 02 ... 720h | PM 03 ... 4320h | PM 04 ... 8640h |
 *                      RESPONSABLES | OBSERVACIONES | COSTOS Promedio | COSTOS Actual
 *
 *   Filas siguientes: cada fila trae AREA y EQUIPO en columnas 0 y 1, por lo que se agrupan
 *   ítems por (area + equipo). Por cada equipo se crea un plan preventivo.
 */
class PlanMantenimientoImporter
{
    public function __construct(public Importacion $importacion) {}

    public function importar(string $rutaCompleta): void
    {
        $this->importacion->update(['estado' => 'procesando']);
        try {
            $hojas = ExcelFacade::toArray(null, $rutaCompleta);
            foreach ($hojas as $idxHoja => $hoja) {
                $this->procesarHoja($idxHoja, $hoja);
            }
            $this->importacion->update(['estado' => 'completada']);
        } catch (\Throwable $e) {
            $this->importacion->update(['estado' => 'fallida', 'resumen' => $e->getMessage()]);
            throw $e;
        }
    }

    private function procesarHoja(int $idxHoja, array $hoja): void
    {
        $this->importacion->total_filas = ($this->importacion->total_filas ?? 0) + count($hoja);

        // Localizar la fila de cabeceras (debe contener AREA + EQUIPO + DESCRIPCION).
        $cabIdx = null;
        $cabeceras = [];
        foreach ($hoja as $i => $fila) {
            $txt = strtoupper(implode('|', array_map(fn ($v) => (string) $v, $fila)));
            if (str_contains($txt, 'AREA') && str_contains($txt, 'EQUIPO') && str_contains($txt, 'DESCRIPCION')) {
                $cabIdx = $i;
                $cabeceras = array_map(fn ($v) => $this->normalizar((string) $v), $fila);
                break;
            }
        }
        if ($cabIdx === null) {
            $this->importacion->save();
            return;
        }

        $idxCol = $this->mapearColumnas($cabeceras);
        $planesPorEquipo = [];   // [equipo_id => PlanMantenimiento]
        $numerosPorEquipo = [];  // [equipo_id => int contador]

        for ($r = $cabIdx + 1; $r < count($hoja); $r++) {
            $fila = $hoja[$r];

            $areaNombre = $this->cortar(strtoupper(trim((string) ($fila[$idxCol['area'] ?? 0] ?? ''))), 60);
            $equipoNombre = $this->cortar(strtoupper(trim((string) ($fila[$idxCol['equipo'] ?? 1] ?? ''))), 150);
            $descripcion = trim((string) ($fila[$idxCol['descripcion'] ?? -1] ?? ''));

            // Saltar filas en blanco (sin descripción ni equipo ni área).
            if ($descripcion === '' && $areaNombre === '' && $equipoNombre === '') continue;

            try {
                if ($areaNombre === '' || $equipoNombre === '' || $descripcion === '') {
                    throw new \RuntimeException('Fila incompleta: faltan AREA, EQUIPO o DESCRIPCION');
                }

                $area = Area::firstOrCreate(
                    ['nombre' => $areaNombre],
                    [
                        'codigo' => $this->cortar(Str::upper(Str::slug($areaNombre, '_')), 60),
                        'activo' => true,
                    ]
                );

                $equipo = Equipo::firstOrCreate(
                    ['area_id' => $area->id, 'nombre' => $equipoNombre],
                    [
                        'codigo' => $this->generarCodigoEquipo($area, $equipoNombre),
                        'activo' => true,
                    ]
                );

                if (!isset($planesPorEquipo[$equipo->id])) {
                    $planesPorEquipo[$equipo->id] = PlanMantenimiento::firstOrCreate(
                        ['equipo_id' => $equipo->id, 'clase' => 'preventivo'],
                        [
                            'codigo' => $this->cortar('IMP-' . $this->importacion->id . '-E' . $equipo->id, 30),
                            'nombre' => $this->cortar($equipoNombre, 200),
                            'area_id' => $area->id,
                            'activo' => true,
                            'creado_por' => $this->importacion->usuario_id,
                        ]
                    );
                    $numerosPorEquipo[$equipo->id] = 0;
                }
                $plan = $planesPorEquipo[$equipo->id];
                $numerosPorEquipo[$equipo->id]++;

                $codigoComp = trim((string) ($fila[$idxCol['codigo'] ?? -1] ?? ''));
                $unidadCod = trim((string) ($fila[$idxCol['unidad'] ?? -1] ?? ''));
                $cantidad = $this->numero($fila[$idxCol['cantidad'] ?? -1] ?? 0);
                $frecuenciaTxt = trim((string) ($fila[$idxCol['frecuencia'] ?? -1] ?? ''));
                $observaciones = trim((string) ($fila[$idxCol['observaciones'] ?? -1] ?? ''));
                $costoProm = $this->numero($fila[$idxCol['costo_promedio'] ?? -1] ?? 0);
                $costoAct = $this->numero($fila[$idxCol['costo_actual'] ?? -1] ?? 0);

                $unidad = $unidadCod
                    ? Unidad::firstOrCreate(
                        ['codigo' => $this->cortar(strtoupper($unidadCod), 50)],
                        ['nombre' => $this->cortar(strtoupper($unidadCod), 80), 'activo' => true]
                    )
                    : null;

                $frecuencia = $frecuenciaTxt
                    ? Frecuencia::firstOrCreate(
                        ['codigo' => $this->cortar(strtoupper(Str::slug($frecuenciaTxt, '_')), 60)],
                        ['nombre' => $this->cortar($frecuenciaTxt, 80), 'activo' => true]
                    )
                    : null;

                $componente = null;
                if ($codigoComp !== '') {
                    $componente = Componente::firstOrCreate(
                        ['equipo_id' => $equipo->id, 'codigo' => $this->cortar($codigoComp, 100)],
                        ['nombre' => $this->cortar($descripcion, 200), 'activo' => true]
                    );
                }

                PlanItem::create([
                    'plan_id' => $plan->id,
                    'componente_id' => $componente?->id,
                    'numero_item' => $numerosPorEquipo[$equipo->id],
                    'descripcion' => $descripcion,
                    'codigo_componente' => $codigoComp ? $this->cortar($codigoComp, 150) : null,
                    'cambio_lubricacion' => $this->cortar((string) ($fila[$idxCol['cambio_lubricacion'] ?? -1] ?? ''), 200) ?: null,
                    'unidad_id' => $unidad?->id,
                    'cantidad' => $cantidad,
                    'frecuencia_id' => $frecuencia?->id,
                    'lila_l_limpieza' => $this->boolDe($fila[$idxCol['l_limpieza'] ?? -1] ?? null),
                    'lila_i_inspeccion' => $this->boolDe($fila[$idxCol['i_inspeccion'] ?? -1] ?? null),
                    'lila_l_lubricacion' => $this->boolDe($fila[$idxCol['l_lubricacion'] ?? -1] ?? null),
                    'lila_a_ajuste' => $this->boolDe($fila[$idxCol['a_ajuste'] ?? -1] ?? null),
                    'pm_360' => $this->boolDe($fila[$idxCol['pm_360'] ?? -1] ?? null),
                    'pm_720' => $this->boolDe($fila[$idxCol['pm_720'] ?? -1] ?? null),
                    'pm_4320' => $this->boolDe($fila[$idxCol['pm_4320'] ?? -1] ?? null),
                    'pm_8640' => $this->boolDe($fila[$idxCol['pm_8640'] ?? -1] ?? null),
                    'observaciones' => $observaciones ?: null,
                    'costo_promedio' => $costoProm,
                    'costo_actual' => $costoAct,
                    'costo_estimado' => $costoProm,
                    'activo' => true,
                ]);
                $this->importacion->increment('filas_importadas');
            } catch (\Throwable $e) {
                $this->importacion->increment('filas_error');
                ImportacionError::create([
                    'importacion_id' => $this->importacion->id,
                    'hoja' => 'Hoja ' . ($idxHoja + 1),
                    'numero_fila' => $r + 1,
                    'mensaje' => Str::limit($e->getMessage(), 490, ''),
                    'datos_originales' => $fila,
                ]);
            }
        }

        $this->importacion->save();
    }

    private function mapearColumnas(array $cabeceras): array
    {
        $map = [];
        foreach ($cabeceras as $i => $cab) {
            $c = strtoupper($cab);
            if (!isset($map['area']) && $c === 'AREA') $map['area'] = $i;
            elseif (!isset($map['equipo']) && $c === 'EQUIPO') $map['equipo'] = $i;
            elseif (str_contains($c, 'COMPONENTE') || str_contains($c, 'CONPONETE')) $map['componente'] = $i;
            elseif ($c === 'ITEM' || str_contains($c, 'ITEM')) $map['item'] = $i;
            elseif (str_contains($c, 'DESCRIP')) $map['descripcion'] = $i;
            elseif (str_contains($c, 'CODIGO')) $map['codigo'] = $i;
            elseif (str_contains($c, 'CANTIDAD')) $map['cantidad'] = $i;
            elseif (str_contains($c, 'UNIDAD')) $map['unidad'] = $i;
            elseif (str_contains($c, 'FRECUENCIA') && str_contains($c, 'LUBRIC')) $map['cambio_lubricacion'] = $i;
            elseif (str_contains($c, 'FRECUEN')) $map['frecuencia'] = $i;
            elseif (str_contains($c, 'RESPONSAB')) $map['responsables'] = $i;
            elseif (str_contains($c, 'OBSERV')) $map['observaciones'] = $i;
            elseif (str_contains($c, 'PROMED')) $map['costo_promedio'] = $i;
            elseif (str_contains($c, 'ACTUAL')) $map['costo_actual'] = $i;
            elseif (str_contains($c, 'PM 01') || str_contains($c, '360')) $map['pm_360'] = $i;
            elseif (str_contains($c, 'PM 02') || str_contains($c, '720')) $map['pm_720'] = $i;
            elseif (str_contains($c, 'PM 03') || str_contains($c, '4320')) $map['pm_4320'] = $i;
            elseif (str_contains($c, 'PM 04') || str_contains($c, '8640')) $map['pm_8640'] = $i;
            elseif (str_contains($c, 'LIMPIEZ')) $map['l_limpieza'] = $i;
            elseif (str_contains($c, 'INSPEC')) $map['i_inspeccion'] = $i;
            elseif (str_contains($c, 'LUBRIC')) $map['l_lubricacion'] = $i;
            elseif (str_contains($c, 'AJUST')) $map['a_ajuste'] = $i;
        }
        return $map;
    }

    private function generarCodigoEquipo(Area $area, string $nombreEquipo): string
    {
        $base = $this->cortar(Str::upper($area->codigo . '_' . Str::slug($nombreEquipo, '_')), 90);
        $codigo = $base;
        $n = 1;
        while (Equipo::where('codigo', $codigo)->exists()) {
            $sufijo = '_' . (++$n);
            $codigo = $this->cortar($base, 100 - mb_strlen($sufijo)) . $sufijo;
        }
        return $codigo;
    }

    private function boolDe(mixed $v): bool
    {
        if ($v === null) return false;
        $s = strtoupper(trim((string) $v));
        if ($s === '') return false;
        return in_array($s, ['X', 'SI', 'SÍ', 'OK', 'TRUE', '1', '✓', 'L', 'I', 'A', 'LU', 'LI', 'LA', 'LILA'], true)
            || str_contains($s, 'LILA') || str_contains($s, 'CAMBIO') || str_contains($s, 'PREDETERMIND');
    }

    private function numero(mixed $v): float
    {
        if ($v === null || $v === '') return 0;
        $s = preg_replace('/[^0-9,.\-]/', '', (string) $v);
        $s = str_replace(',', '.', $s ?? '');
        return (float) ($s ?: 0);
    }

    private function normalizar(string $s): string
    {
        return trim(preg_replace('/\s+/', ' ', $s));
    }

    private function cortar(?string $s, int $max): string
    {
        $s = trim((string) $s);
        return mb_substr($s, 0, $max);
    }
}
