<?php

class Kardex extends Model
{
    public function __construct()
    {
        parent::__construct();
    }

    public function insert(array $d): array
    {

        $t0 = microtime(true);

        // 1) Validación
        $fields = $this->validate($d);
        if (!empty($fields)) {
            return $this->respError('VALIDATION_ERROR', 'Datos inválidos.', ['fields' => $fields], $t0);
        }

        // 2) Normalización
        foreach (['cantidad_entrada', 'cantidad_salida', 'costo_unitario', 'costo_total', 'stock'] as $k) {
            $d[$k] = (float) str_replace(',', '.', (string) ($d[$k] ?? '0'));
        }
        if (!isset($d['costo_total'])) {
            $qty = max((float) $d['cantidad_entrada'], (float) $d['cantidad_salida']);
            $d['costo_total'] = round($qty * (float) $d['costo_unitario'], 2);
        }

        // 3) Insert
        try {
            $sql = "INSERT INTO kardex
        (id_usuario, tipo_kardex, tipo_envio_sunat, id_presentacion, tipo_presentacion,
         cantidad_entrada, cantidad_salida, concepto, costo_unitario,
         costo_total, fecha_registro, serie_documento, numero_documento,
         tipo_documento, stock, estado)
       VALUES
        (:id_usuario, :tipo_kardex, :tipo_envio_sunat, :id_presentacion, :tipo_presentacion,
         :cantidad_entrada, :cantidad_salida, :concepto, :costo_unitario,
         :costo_total, NOW(), :serie_documento, :numero_documento,
         :tipo_documento, :stock, :estado)";

            $st = $this->db->prepare($sql);

            $st->bindValue(':id_usuario', (int) $d['id_usuario'], PDO::PARAM_INT);
            $st->bindValue(':tipo_kardex', (string) $d['tipo_kardex']);
            $st->bindValue(':tipo_envio_sunat', (string) $d['tipo_envio_sunat']);
            $st->bindValue(':id_presentacion', (int) $d['id_presentacion'], PDO::PARAM_INT);
            $st->bindValue(':tipo_presentacion', (int) $d['tipo_presentacion'], PDO::PARAM_INT);

            $st->bindValue(':cantidad_entrada', number_format((float) $d['cantidad_entrada'], 3, '.', ''));
            $st->bindValue(':cantidad_salida', number_format((float) $d['cantidad_salida'], 3, '.', ''));
            $st->bindValue(':concepto', (string) ($d['concepto'] ?? ''));

            $st->bindValue(':costo_unitario', number_format((float) $d['costo_unitario'], 2, '.', ''));
            $st->bindValue(':costo_total', number_format((float) $d['costo_total'], 2, '.', ''));

            $this->bindNullable($st, ':serie_documento', $d['serie_documento'] ?? null);
            $this->bindNullable($st, ':numero_documento', $d['numero_documento'] ?? null);
            $this->bindNullable($st, ':tipo_documento', $d['tipo_documento'] ?? null);

            $st->bindValue(':stock', number_format((float) $d['stock'], 3, '.', ''));
            $st->bindValue(':estado', (string) $d['estado']);

            $st->execute();

            return $this->respOk(['message' => 'Se agregó correctamente.'], $t0);

        } catch (\PDOException $e) {
            $ei = $e->errorInfo; // [sqlstate, driver_code, driver_msg]
            if (($ei[1] ?? null) === 1062) {
                return $this->respError('DB_DUP_ENTRY', 'Registro duplicado.', [
                    'details' => [
                        'sqlstate' => $ei[0] ?? null,
                        'driver_code' => $ei[1] ?? null,
                        'driver_message' => $ei[2] ?? null,
                    ]
                ], $t0);
            }
            return $this->respError('DB_ERROR', 'Error al guardar en la base de datos.', [
                'details' => [
                    'message' => $e->getMessage(),
                    'sqlstate' => $ei[0] ?? null,
                    'driver_code' => $ei[1] ?? null,
                    'driver_message' => $ei[2] ?? null,
                ]
            ], $t0);

        } catch (\Throwable $e) {
            return $this->respError('UNEXPECTED_ERROR', 'Ocurrió un error inesperado.', [
                'details' => ['message' => $e->getMessage()]
            ], $t0);
        }
    }

    // ---------- helpers ----------
    private function validate(array $d): array
    {
        $e = [];
        if (empty($d['id_usuario']))
            $e[] = ['field' => 'id_usuario', 'error' => 'requerido'];
        if (!isset($d['id_presentacion']))
            $e[] = ['field' => 'id_presentacion', 'error' => 'requerido'];
        if (!isset($d['tipo_presentacion']))
            $e[] = ['field' => 'tipo_presentacion', 'error' => 'requerido'];

        $tipos = [1, 2, 3, 4, 5, 6];
        if (empty($d['tipo_kardex']) || !in_array($d['tipo_kardex'], $tipos, true)) {
            $e[] = ['field' => 'tipo_kardex', 'error' => 'valor inválido'];
        }
        $env = ['prueba', 'produccion'];
        if (empty($d['tipo_envio_sunat']) || !in_array($d['tipo_envio_sunat'], $env, true)) {
            $e[] = ['field' => 'tipo_envio_sunat', 'error' => 'valor inválido'];
        }
        $est = ['activo', 'anulado'];
        if (empty($d['estado']) || !in_array($d['estado'], $est, true)) {
            $e[] = ['field' => 'estado', 'error' => 'valor inválido'];
        }

        foreach (['cantidad_entrada', 'cantidad_salida', 'costo_unitario', 'costo_total', 'stock'] as $k) {
            if (isset($d[$k]) && !is_numeric(str_replace(',', '.', (string) $d[$k]))) {
                $e[] = ['field' => $k, 'error' => 'debe ser numérico'];
            }
        }
        return $e;
    }

    private function bindNullable(\PDOStatement $st, string $param, $value): void
    {
        if ($value === null || $value === '')
            $st->bindValue($param, null, PDO::PARAM_NULL);
        else
            $st->bindValue($param, (string) $value, PDO::PARAM_STR);
    }

    private function iso8601(): string
    {
        return gmdate('Y-m-d\TH:i:s\Z');
    }

    private function respOk(array $data, float $t0): array
    {
        return [
            'ok' => true,
            'data' => $data,
            'meta' => [
                'timestamp' => $this->iso8601(),
                'duration_ms' => (int) round((microtime(true) - $t0) * 1000),
            ],
        ];
    }

    private function respError(string $code, string $message, array $extras, float $t0): array
    {
        return [
            'ok' => false,
            'code' => $code,
            'message' => $message,
            'meta' => [
                'timestamp' => $this->iso8601(),
                'duration_ms' => (int) round((microtime(true) - $t0) * 1000),
            ],
        ] + $extras;
    }

}