<?php

declare(strict_types=1);

if ($argc < 2) {
    fwrite(STDERR, "Usage: php scripts/export_company_data_sql.php /path/dump.sql [output.sql]\n");
    exit(1);
}

$source = $argv[1];
$output = $argv[2] ?? base_path_like(__DIR__.'/../database/exports/luxealmacen_empresa_5_usuario_17.sql');

if (! is_file($source)) {
    fwrite(STDERR, "Source SQL not found: {$source}\n");
    exit(1);
}

@mkdir(dirname($output), 0775, true);

$companyId = 5;
$primaryUserId = 17;
$sql = file_get_contents($source);

if ($sql === false) {
    fwrite(STDERR, "Could not read source SQL: {$source}\n");
    exit(1);
}

preg_match_all('/INSERT INTO `([^`]+)` \((.*?)\) VALUES\s*(.*?);/s', $sql, $matches, PREG_SET_ORDER);

$tables = [];
$order = [];

foreach ($matches as $match) {
    $table = $match[1];
    $columns = array_map(
        static fn (string $column): string => trim($column, " `\t\n\r\0\x0B"),
        explode(',', $match[2])
    );

    $order[] = $table;
    $tables[$table]['columns'] = $columns;
    $tables[$table]['rows'] ??= [];

    foreach (splitTuples($match[3]) as $tuple) {
        $values = splitValues(trim($tuple, "() \n\r\t"));
        $assoc = [];

        foreach ($columns as $index => $column) {
            $assoc[$column] = parseSqlValue($values[$index] ?? 'NULL');
        }

        $id = isset($assoc['id']) ? (string) $assoc['id'] : sha1($tuple);
        $tables[$table]['rows'][$id] = [
            'assoc' => $assoc,
            'tuple' => $tuple,
        ];
    }
}

$selected = [];
$sets = [
    'empresas' => [$companyId => true],
    'usuarios' => [$primaryUserId => true],
    'paises' => [],
    'ciudades' => [],
    'roles' => [],
    'permisos' => [],
    'planes' => [],
    'sucursales' => [],
    'cajas' => [],
    'categorias' => [],
    'proveedores' => [],
    'marcas' => [],
    'unidades_medida' => [],
    'impuestos' => [],
    'clientes' => [],
    'productos' => [],
    'producto_variantes' => [],
    'aperturas_caja' => [],
    'cierres_caja' => [],
    'movimientos_caja' => [],
    'ventas' => [],
    'facturas' => [],
    'compras' => [],
    'ordenes_compra' => [],
    'recepciones_compra' => [],
    'soporte_tickets' => [],
    'suscripciones' => [],
    'ai_help_sessions' => [],
];

for ($pass = 0; $pass < 8; $pass++) {
    foreach ($tables as $table => $payload) {
        foreach ($payload['rows'] as $id => $row) {
            $data = $row['assoc'];

            if (shouldSelectDirectly($data, $companyId, $primaryUserId)) {
                selectRow($selected, $sets, $table, $id, $data);
            }

            if (isset($data['id']) && ! empty($sets[$table][(int) $data['id']])) {
                selectRow($selected, $sets, $table, $id, $data);
            }

            selectByRelationships($selected, $sets, $table, $id, $data);
        }
    }
}

$exportTables = orderedExportTables($order);
$lines = [
    '-- Veltrix POS partial data export',
    '-- Source: '.$source,
    '-- Scope: empresa_id = 5 (Luxealmacen) OR usuario_id = 17',
    '-- Generated: '.date('Y-m-d H:i:s'),
    '',
    'SET FOREIGN_KEY_CHECKS=0;',
    'SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";',
    '',
];

$counts = [];

foreach ($exportTables as $table) {
    if (empty($selected[$table]) || empty($tables[$table]['columns'])) {
        continue;
    }

    $columns = $tables[$table]['columns'];
    $tuples = [];

    foreach (array_keys($selected[$table]) as $id) {
        if (isset($tables[$table]['rows'][$id])) {
            $tuples[] = $tables[$table]['rows'][$id]['tuple'];
        }
    }

    if ($tuples === []) {
        continue;
    }

    $counts[$table] = count($tuples);
    $lines[] = '-- '.$table.' ('.count($tuples).' rows)';
    $lines[] = 'INSERT INTO `'.$table.'` (`'.implode('`, `', $columns).'`) VALUES';
    $lines[] = implode(",\n", $tuples).';';
    $lines[] = '';
}

$lines[] = 'SET FOREIGN_KEY_CHECKS=1;';
$lines[] = '';

file_put_contents($output, implode("\n", $lines));

echo "Export written: {$output}\n";
echo "Tables exported: ".count($counts)."\n";

foreach ($counts as $table => $count) {
    echo str_pad($table, 32).$count."\n";
}

function base_path_like(string $path): string
{
    return preg_replace('#/scripts/\.\./#', '/', $path) ?: $path;
}

function orderedExportTables(array $dumpOrder): array
{
    $parentFirst = [
        'paises',
        'ciudades',
        'planes',
        'empresas',
        'roles',
        'permisos',
        'rol_permiso',
        'sucursales',
        'cajas',
        'usuarios',
        'usuario_empresa',
        'suscripciones',
        'configuracion_empresa',
        'numeraciones_documentos',
        'impuestos',
        'metodos_pago',
        'unidades_medida',
        'categorias',
        'marcas',
        'proveedores',
        'clientes',
        'productos',
        'producto_variantes',
        'inventario',
        'aperturas_caja',
        'cierres_caja',
        'movimientos_caja',
        'ordenes_compra',
        'orden_compra_detalles',
        'recepciones_compra',
        'recepcion_compra_detalles',
        'compras',
        'compra_detalles',
        'pagos_compra',
        'ventas',
        'venta_detalles',
        'venta_pagos',
        'facturas',
        'factura_detalles',
        'movimientos_inventario',
        'soporte_tickets',
        'soporte_ticket_eventos',
        'ai_help_sessions',
        'ai_help_messages',
        'pagos_suscripcion',
    ];

    $dumpTables = array_values(array_unique($dumpOrder));
    $known = array_values(array_intersect($parentFirst, $dumpTables));
    $remaining = array_values(array_diff($dumpTables, $known));

    return array_merge($known, $remaining);
}

function splitTuples(string $values): array
{
    $tuples = [];
    $length = strlen($values);
    $depth = 0;
    $inString = false;
    $buffer = '';

    for ($i = 0; $i < $length; $i++) {
        $char = $values[$i];
        $prev = $i > 0 ? $values[$i - 1] : '';

        if ($char === "'" && $prev !== '\\') {
            $inString = ! $inString;
        }

        if (! $inString && $char === '(') {
            $depth++;
        }

        if ($depth > 0) {
            $buffer .= $char;
        }

        if (! $inString && $char === ')') {
            $depth--;

            if ($depth === 0) {
                $tuples[] = trim($buffer);
                $buffer = '';
            }
        }
    }

    return $tuples;
}

function splitValues(string $tuple): array
{
    $values = [];
    $length = strlen($tuple);
    $inString = false;
    $buffer = '';

    for ($i = 0; $i < $length; $i++) {
        $char = $tuple[$i];
        $prev = $i > 0 ? $tuple[$i - 1] : '';

        if ($char === "'" && $prev !== '\\') {
            $inString = ! $inString;
        }

        if (! $inString && $char === ',') {
            $values[] = trim($buffer);
            $buffer = '';
            continue;
        }

        $buffer .= $char;
    }

    $values[] = trim($buffer);

    return $values;
}

function parseSqlValue(string $value): mixed
{
    $value = trim($value);

    if (strcasecmp($value, 'NULL') === 0) {
        return null;
    }

    if (str_starts_with($value, "'") && str_ends_with($value, "'")) {
        return stripcslashes(substr($value, 1, -1));
    }

    if (is_numeric($value)) {
        return str_contains($value, '.') ? (float) $value : (int) $value;
    }

    return $value;
}

function shouldSelectDirectly(array $data, int $companyId, int $primaryUserId): bool
{
    foreach (['empresa_id'] as $column) {
        if ((int) ($data[$column] ?? 0) === $companyId) {
            return true;
        }
    }

    foreach (['usuario_id', 'creado_por_usuario_id', 'asignado_a_usuario_id', 'anulado_por_usuario_id'] as $column) {
        if ((int) ($data[$column] ?? 0) === $primaryUserId) {
            return true;
        }
    }

    return false;
}

function selectRow(array &$selected, array &$sets, string $table, string|int $rowKey, array $data): void
{
    $selected[$table][(string) $rowKey] = true;

    if (isset($data['id'])) {
        $sets[$table][(int) $data['id']] = true;
    }

    foreach ([
        'empresa_id' => 'empresas',
        'usuario_id' => 'usuarios',
        'creado_por_usuario_id' => 'usuarios',
        'asignado_a_usuario_id' => 'usuarios',
        'anulado_por_usuario_id' => 'usuarios',
        'sucursal_id' => 'sucursales',
        'caja_id' => 'cajas',
        'categoria_id' => 'categorias',
        'proveedor_id' => 'proveedores',
        'marca_id' => 'marcas',
        'unidad_medida_id' => 'unidades_medida',
        'cliente_id' => 'clientes',
        'producto_id' => 'productos',
        'variante_id' => 'producto_variantes',
        'apertura_id' => 'aperturas_caja',
        'apertura_caja_id' => 'aperturas_caja',
        'venta_id' => 'ventas',
        'factura_id' => 'facturas',
        'compra_id' => 'compras',
        'orden_compra_id' => 'ordenes_compra',
        'orden_detalle_id' => 'orden_compra_detalles',
        'recepcion_id' => 'recepciones_compra',
        'recepcion_compra_id' => 'recepciones_compra',
        'ticket_id' => 'soporte_tickets',
        'session_id' => 'ai_help_sessions',
        'suscripcion_id' => 'suscripciones',
        'plan_id' => 'planes',
        'rol_id' => 'roles',
        'permiso_id' => 'permisos',
        'pais_id' => 'paises',
        'ciudad_id' => 'ciudades',
    ] as $column => $targetTable) {
        if (isset($data[$column]) && $data[$column] !== null) {
            $sets[$targetTable][(int) $data[$column]] = true;
        }
    }
}

function selectByRelationships(array &$selected, array &$sets, string $table, string|int $id, array $data): void
{
    $relations = [
        'usuario_empresa' => [
            'usuario_id' => 'usuarios',
            'empresa_id' => 'empresas',
        ],
        'rol_permiso' => [
            'rol_id' => 'roles',
            'permiso_id' => 'permisos',
        ],
        'factura_detalles' => [
            'factura_id' => 'facturas',
        ],
        'venta_detalles' => [
            'venta_id' => 'ventas',
        ],
        'venta_pagos' => [
            'venta_id' => 'ventas',
        ],
        'compra_detalles' => [
            'compra_id' => 'compras',
        ],
        'orden_compra_detalles' => [
            'orden_compra_id' => 'ordenes_compra',
        ],
        'recepcion_compra_detalles' => [
            'recepcion_id' => 'recepciones_compra',
        ],
        'soporte_ticket_eventos' => [
            'ticket_id' => 'soporte_tickets',
        ],
        'pagos_compra' => [
            'compra_id' => 'compras',
        ],
        'pagos_suscripcion' => [
            'suscripcion_id' => 'suscripciones',
        ],
        'ai_help_messages' => [
            'session_id' => 'ai_help_sessions',
        ],
    ];

    foreach ($relations[$table] ?? [] as $column => $parentTable) {
        if (isset($data[$column]) && ! empty($sets[$parentTable][(int) $data[$column]])) {
            selectRow($selected, $sets, $table, $id, $data);
            return;
        }
    }
}
