<?php

namespace app\controllers;

use Yii; 
use app\models\Clientes;
use app\models\Pedidos;
use app\models\Productos;
use app\models\PedidosSearch;
use app\models\Cotizaciones;
use app\models\CotizacionItems;
use app\models\PedidoReportePrecios;
use app\models\DocumentosPedido;
use yii\helpers\ArrayHelper;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
use yii\web\UploadedFile;
use OpenSpout\Reader\XLSX\Reader; 
use yii\data\ActiveDataProvider; 

class PedidosController extends Controller
{
    public function behaviors()
    {
        return array_merge(
            parent::behaviors(),
            [
                'verbs' => [
                    'class' => VerbFilter::className(),
                    'actions' => [
                        'delete' => ['POST'],
                    ],
                ],
            ]
        );
    }

    public function actionIndex()
    {
        $searchModel = new PedidosSearch();
        $dataProvider = $searchModel->search($this->request->queryParams);

        return $this->render('index', [
            'searchModel' => $searchModel,
            'dataProvider' => $dataProvider,
        ]);
    }

    public function actionView($id)
    {
        return $this->render('view', [
            'model' => $this->findModel($id),
        ]);
    }

    /**
     * Crea un nuevo Pedido y procesa el archivo Excel.
     * CRÍTICO: Asigna CLAC/TBS al campo `tipo_documento_asignado` de los productos NEWCODE.
     */
    public function actionCreate()
    {
        $model = new Pedidos();
        $model->scenario = 'create'; 

        if (Yii::$app->request->isPost) {
            $model->load(Yii::$app->request->post());
            $model->archivoExcel = UploadedFile::getInstance($model, 'archivoExcel');

            if ($model->validate(['termino_busqueda_cliente', 'archivoExcel'])) {
                
                $termino = $model->termino_busqueda_cliente;
                $cliente = Clientes::findOne(['ruc' => $termino]) ?: Clientes::find()->where(['like', 'nombre_razon_social', $termino])->one();

                if ($cliente === null) {
                    Yii::$app->session->setFlash('error', "Error: No se encontró ningún cliente que coincida con '$termino'.");
                    return $this->render('create', ['model' => $model]);
                }
                
                $rutaDirectorio = Yii::getAlias('@webroot/uploads');
                if (!is_dir($rutaDirectorio)) { mkdir($rutaDirectorio, 0777, true); }
                $rutaArchivo = $rutaDirectorio . '/' . uniqid() . '.' . $model->archivoExcel->extension;

                $transaction = Yii::$app->db->beginTransaction();
                try {
                    if (!$model->archivoExcel->saveAs($rutaArchivo)) {
                        throw new \Exception('Error al guardar el archivo subido.');
                    }

                    $model->cliente_id = $cliente->id; 
                    $model->estado = 'reporte_generado';
                    if (!$model->save(false)) { 
                        throw new \Exception('No se pudo crear el registro del pedido.');
                    }
                    
                    // INICIALIZAR EL CONTADOR DE NEWCODE AL INICIO DEL PROCESO
                    $currentNewCodeNumber = $this->obtenerUltimoNumeroNewCode();
                    
                    $reader = new Reader();
                    $reader->open($rutaArchivo);
                    $filasProcesadas = 0;

                    foreach ($reader->getSheetIterator() as $sheet) {
                        foreach ($sheet->getRowIterator() as $row) {
                            if ($filasProcesadas === 0) { $filasProcesadas++; continue; } 

                            $cells = $row->getCells();
                            $codigoProducto = trim($cells[0]->getValue());
                            $descripcionExcel = isset($cells[1]) ? trim($cells[1]->getValue()) : ''; 
                            $cantidad = isset($cells[2]) ? (int)trim($cells[2]->getValue()) : 0;
                            
                            if (empty($codigoProducto) && empty($descripcionExcel)) continue; 
                            
                            $producto = Productos::findOne(['codigo_producto' => $codigoProducto]);

                            if ($producto === null) {
                                // INCREMENTAR EL CONTADOR Y GENERAR NUEVO CÓDIGO
                                $currentNewCodeNumber++;
                                $newCode = 'newcode' . $currentNewCodeNumber;
                                
                                // VERIFICAR QUE NO EXISTA (medida de seguridad adicional)
                                while (Productos::findOne(['codigo_producto' => $newCode]) !== null) {
                                    $currentNewCodeNumber++;
                                    $newCode = 'newcode' . $currentNewCodeNumber;
                                }
                                
                                $newProduct = new Productos();
                                $newProduct->codigo_producto = $newCode;
                                
                                if (empty($codigoProducto)) {
                                    // Asignación CLAC para ítems sin código en Excel
                                    $newProduct->tipo_documento_asignado = 'CLAC'; 
                                } else {
                                    // Asignación TBS para ítems con código en Excel pero no en DB
                                    $newProduct->tipo_documento_asignado = 'TBS'; 
                                }

                                $newProduct->descripcion = !empty($descripcionExcel) ? $descripcionExcel : 'Producto Nuevo - Descripción Pendiente';
                                $newProduct->status = 'pendiente'; 
                                
                                if (!$newProduct->save()) {
                                    $errores = implode(', ', array_map('current', $newProduct->getErrors()));
                                    throw new \Exception("Error al crear NEWCODE ({$newCode}). Errores: $errores");
                                }
                                $producto = $newProduct;
                            } 

                            // Buscar los 3 últimos precios
                            $ultimosPrecios = CotizacionItems::find()
                                ->joinWith('cotizacion') 
                                ->where(['producto_id' => $producto->id])
                                ->orderBy(['cotizaciones.fecha_cotizacion' => SORT_DESC])
                                ->limit(3)
                                ->all();
                            
                            // Guardar en el reporte
                            $reporte = new PedidoReportePrecios();
                            $reporte->pedido_id = $model->id; 
                            $reporte->producto_id = $producto->id; 
                            $reporte->cantidad = $cantidad; 
                            
                            if (isset($ultimosPrecios[0])) {
                                $reporte->precio_1 = $ultimosPrecios[0]->precio_unitario_fob;
                                $reporte->orden_1 = $ultimosPrecios[0]->cotizacion->numero_orden;
                                $reporte->fecha_1 = $ultimosPrecios[0]->cotizacion->fecha_cotizacion;
                            }
                            if (isset($ultimosPrecios[1])) {
                                $reporte->precio_2 = $ultimosPrecios[1]->precio_unitario_fob;
                                $reporte->orden_2 = $ultimosPrecios[1]->cotizacion->numero_orden;
                                $reporte->fecha_2 = $ultimosPrecios[1]->cotizacion->fecha_cotizacion;
                            }
                            if (isset($ultimosPrecios[2])) {
                                $reporte->precio_3 = $ultimosPrecios[2]->precio_unitario_fob;
                                $reporte->orden_3 = $ultimosPrecios[2]->cotizacion->numero_orden;
                                $reporte->fecha_3 = $ultimosPrecios[2]->cotizacion->fecha_cotizacion;
                            }
                            
                            if (!$reporte->save()) {
                                $errores = implode(', ', array_map('current', $reporte->getErrors()));
                                throw new \Exception('No se pudo guardar el reporte de precios. Errores: ' . $errores);
                            }
                        }
                    }
                    $reader->close();

                    $transaction->commit();
                    Yii::$app->session->setFlash('success', 'Archivo procesado. Reporte de precios generado.');
                    
                    return $this->redirect(['ver-reporte', 'id' => $model->id]);

                } catch (\Exception $e) {
                    $transaction->rollBack();
                    Yii::$app->session->setFlash('error', 'Error al procesar: '. $e->getMessage());
                } finally {
                    if (isset($rutaArchivo) && file_exists($rutaArchivo)) {
                        unlink($rutaArchivo);
                    }
                }
            }
        }

        return $this->render('create', [
            'model' => $model,
        ]);
    }
    
    /**
     * Método auxiliar para obtener el último número de newcode en la base de datos
     * @return int El número más alto encontrado, o 0 si no hay ninguno
     */
    protected function obtenerUltimoNumeroNewCode()
    {
        $lastNcProduct = Productos::find()
            ->select('codigo_producto')
            ->where(['like', 'codigo_producto', 'newcode%', false])
            ->orderBy(['CAST(SUBSTRING(codigo_producto, 8) AS UNSIGNED)' => SORT_DESC]) // Ordenar numéricamente
            ->limit(1)
            ->one();
        
        if ($lastNcProduct && preg_match('/newcode(\d+)$/i', $lastNcProduct->codigo_producto, $matches)) {
            return (int)$matches[1];
        }
        
        return 0;
    }
    
    public function actionVerReporte($id)
    {
        $pedido = $this->findModel($id);
        $query = PedidoReportePrecios::find()
            ->where(['pedido_id' => $id])
            ->with(['producto']); 

        $dataProvider = new ActiveDataProvider([
            'query' => $query,
            'pagination' => ['pageSize' => 50],
        ]);

        return $this->render('ver-reporte', [
            'pedido' => $pedido,
            'dataProvider' => $dataProvider,
        ]);
    }

    public function actionDelete($id)
    {
        $this->findModel($id)->delete();
        return $this->redirect(['index']);
    }

   /**
    * Genera los documentos CLAC y TBS agrupando los ítems pendientes.
    * 1. Separar CLAC vs. TBS (campo `tipo_documento_asignado`).
    * 2. Separar NEWCODE vs. HISTÓRICO.
    * 3. Agrupar en lotes de 20 (20x20) cada subgrupo.
    */
    public function actionGenerarClac($pedido_id)
    {
        if (!Yii::$app->request->isPost) {
            return $this->redirect(['ver-reporte', 'id' => $pedido_id]);
        }
        
        $pedido = $this->findModel($pedido_id); 

        $itemsReporte = PedidoReportePrecios::find()
            ->where(['pedido_id' => $pedido_id])
            ->andWhere(['documento_id' => null])
            ->with('producto') 
            ->all();

        if (empty($itemsReporte)) {
            Yii::$app->session->setFlash('warning', 'Advertencia: No hay ítems pendientes de agrupamiento CLAC/TBS.');
            return $this->redirect(['ver-reporte', 'id' => $pedido_id]);
        }
        
        $totalDocumentosGenerados = 0;
        $documentosGenerados = [];
        
        // 2. AGRUPAR POR CLASIFICACIÓN Y POR TIPO (Newcode vs. Histórico)
        $gruposDocumento = [
            'CLAC_NEWCODE' => [], 
            'CLAC_HISTORICO' => [],
            'TBS_NEWCODE' => [],
            'TBS_HISTORICO' => [],
        ];
        
        $transaction = Yii::$app->db->beginTransaction();

        try {
            foreach ($itemsReporte as $item) {
                // Obtener el tipo de documento asignado (CLAC o TBS)
                $clasificacion_raw = $item->producto->tipo_documento_asignado ?? '';
                $clasificacion = trim(strtoupper($clasificacion_raw)); 
                
                // Determinar si es un producto NEWCODE
                $esNewCode = str_starts_with(strtolower($item->producto->codigo_producto), 'newcode');
                
                $sufijo = $esNewCode ? '_NEWCODE' : '_HISTORICO';
                
                if ($clasificacion === 'CLAC') {
                    $gruposDocumento['CLAC' . $sufijo][] = $item;
                } elseif ($clasificacion === 'TBS') {
                    $gruposDocumento['TBS' . $sufijo][] = $item;
                }
            } 
            
            $encontrados = count($gruposDocumento['CLAC_NEWCODE']) + count($gruposDocumento['CLAC_HISTORICO']) + count($gruposDocumento['TBS_NEWCODE']) + count($gruposDocumento['TBS_HISTORICO']);
            
            if ($encontrados === 0) {
                 throw new \Exception('No se encontraron productos con clasificación CLAC o TBS entre los ítems pendientes. Verifique la tabla Productos.');
            }
            
            // --- FASE 3: GENERACIÓN DE DOCUMENTOS POR LOTES (20x20) ---

            foreach ($gruposDocumento as $claveGrupo => $itemsDelGrupo) {
                if (empty($itemsDelGrupo)) continue;

                $partes = explode('_', $claveGrupo);
                $tipoDocumento = $partes[0]; // CLAC o TBS

                // Aplicar la regla de agrupación de 20x20
                $lotesDe20 = array_chunk($itemsDelGrupo, 20); 

                foreach ($lotesDe20 as $lote) {
                    $numeroDocumentoFinal = ''; 
                    $statusDocumento = $tipoDocumento . ' GENERADA';
                    
                    // A. Generar Correlativo Seguro
                    $lastDoc = DocumentosPedido::find()
                        ->select('numero_documento')
                        ->where(['like', 'numero_documento', $tipoDocumento . '%', false]) 
                        ->orderBy(['numero_documento' => SORT_DESC])
                        ->limit(1)
                        ->one();

                    $nextNumber = 1; 
                    if ($lastDoc) {
                        $lastNumber = (int)substr($lastDoc->numero_documento, strlen($tipoDocumento));
                        $nextNumber = $lastNumber + 1;
                    }
                    
                    $numeroDocumentoFinal = $tipoDocumento . str_pad($nextNumber, 4, '0', STR_PAD_LEFT); 

                    // B. Crear Documento Maestro (DocumentosPedido)
                    $documentoMaestro = new DocumentosPedido();
                    $documentoMaestro->pedido_id = $pedido->id;
                    $documentoMaestro->tipo_documento = $tipoDocumento;
                    $documentoMaestro->status_documento = $statusDocumento;
                    $documentoMaestro->numero_documento = $numeroDocumentoFinal;
                    $documentoMaestro->fecha_creacion = date('Y-m-d H:i:s');
                    
                    if (!$documentoMaestro->save()) {
                        throw new \Exception('Error al guardar documento maestro: ' . print_r($documentoMaestro->errors, true));
                    }
                    
                    // C. CREAR COTIZACIÓN ASOCIADA 
                    $cotizacion = new Cotizaciones();
                    $cotizacion->numero_orden = $numeroDocumentoFinal; 
                    $cotizacion->cliente_id = $pedido->cliente_id;
                    $cotizacion->fecha_cotizacion = date('Y-m-d H:i:s');
                    $cotizacion->estado = $statusDocumento; 
                    $cotizacion->tipo_documento = $tipoDocumento; 
                    
                    if (!$cotizacion->save()) {
                        throw new \Exception('Error al guardar cotización asociada: ' . print_r($cotizacion->errors, true));
                    }
                    
                    $lineaCounter = 1; // Inicializar contador de líneas por documento
                    
                    // D. Crear Ítems de la Cotización y Vincular Ítems del Reporte
                    foreach ($lote as $itemReporte) {
                         // Crear Ítem de Cotización
                         $cotizacionItem = new CotizacionItems();
                         $cotizacionItem->cotizacion_id = $cotizacion->id;
                         $cotizacionItem->producto_id = $itemReporte->producto_id;
                         
                         // *** ASIGNACIÓN DE CANTIDAD (REQUERIDO) ***
                         $cotizacionItem->cantidad = $itemReporte->cantidad; 
                         
                         // *** ASIGNACIÓN DE LINEA ITEM (OPCIONAL EN MODELO) ***
                         $cotizacionItem->linea_item = $lineaCounter++; 
                         
                         // *** ASIGNACIÓN DE PRECIO FOB (OPCIONAL EN MODELO) ***
                         // Usar el precio 1 del reporte, si es NULL usar '0.00'.
                         $cotizacionItem->precio_unitario_fob = $itemReporte->precio_1 ?? '0.00'; 
                         
                         // 'cantidad_cartones' y 'precio_unitario_cif' se dejan sin asignar (NULL en DB).
                         
                         if (!$cotizacionItem->save()) {
                            // Este error solo ocurrirá si falla la validación o si la DB NO permite NULL en un campo que dejamos vacío.
                            throw new \Exception('Error al guardar item de cotización: ' . print_r($cotizacionItem->getErrors(), true));
                         }
                         
                         // Vincular el Ítem del Reporte al Documento Maestro
                         $itemReporte->documento_id = $documentoMaestro->id;
                         $itemReporte->status_documento = $statusDocumento;
                         if (!$itemReporte->save(false)) { 
                             throw new \Exception('Error al actualizar el ítem del reporte con el ID de documento.');
                         }
                    }

                    // E. Enviar Alerta por Correo (Solo para CLAC)
                    if ($tipoDocumento === 'CLAC') {
                        $this->enviarAlertaDocumento($documentoMaestro);
                    }

                    $totalDocumentosGenerados++;
                    $documentosGenerados[] = $numeroDocumentoFinal;
                }
            }
            
            $transaction->commit();

        } catch (\Exception $e) {
            $transaction->rollBack();
            $mensaje = strpos($e->getMessage(), 'CLAC o TBS') !== false ? $e->getMessage() : "Error fatal durante la generación: " . $e->getMessage();
            Yii::$app->session->setFlash('error', $mensaje);
            return $this->redirect(['ver-reporte', 'id' => $pedido_id]);
        } 

        if ($totalDocumentosGenerados > 0) {
            $listaDocs = implode(', ', $documentosGenerados);
            Yii::$app->session->setFlash('success', "¡Proceso finalizado! Se generaron $totalDocumentosGenerados documentos ($listaDocs).");
        } else {
             Yii::$app->session->setFlash('info', 'No se generaron nuevos documentos.');
        }
        
        return $this->redirect(['/cotizaciones/index']);
    }

    protected function enviarAlertaDocumento(DocumentosPedido $documento)
    {
        $destinatario = 'clac.revisor@empresa.com'; 
        $asunto = "NUEVO DOCUMENTO {$documento->tipo_documento} PENDIENTE DE REVISIÓN: {$documento->numero_documento}";
        $linkRevision = Yii::$app->urlManager->createAbsoluteUrl(['pedidos/revisar-documento', 'id' => $documento->id]);

        try {
            Yii::$app->mailer->compose('documento-alerta', [
                'documento' => $documento, 
                'linkRevision' => $linkRevision
            ])
            ->setFrom([Yii::$app->params['senderEmail'] ?? 'no-responder@dominio.com' => Yii::$app->params['senderName'] ?? 'Sistema'])
            ->setTo($destinatario)
            ->setSubject($asunto)
            ->send();
            Yii::warning("Alerta de correo enviada para documento: {$documento->numero_documento}");
        } catch (\Exception $e) {
            Yii::error("Error al enviar correo para {$documento->numero_documento}: " . $e->getMessage());
        }
    }
 
    protected function findModel($id)
    {
        if (($model = Pedidos::findOne(['id' => $id])) !== null) {
            return $model;
        }

        throw new NotFoundHttpException('El pedido solicitado no existe.');
    }
}