<?php

namespace App\Services;

use Closure;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;

class SqliteExportService
{
    protected string $connection;

    public function __construct(string $connection)
    {
        $this->connection = $connection;
    }

    /**
     * 匯出單一模型資料到 SQLite 表。
     *
     * @param class-string<Model> $modelClass
     * @param string $tableName
     * @param Closure(Blueprint): void $tableSchema
     * @param Closure(Model): array $transformer
     * @param int $chunkSize
     */
    public function exportTableFromModel(
        string $modelClass,
        string $tableName,
        Closure $tableSchema,
        Closure $transformer,
        int $chunkSize = 1000
    ): void {
        $this->dropAndCreateTable($tableName, $tableSchema);

        $modelInstance = new $modelClass;

        $modelInstance->newQuery()->orderBy('id')->chunk($chunkSize, function (Collection $chunk) use ($tableName, $transformer) {
            $rows = $chunk->map($transformer)->toArray();
            $this->insertData($tableName, $rows);
        });
        $this->purge();
    }

    /**
     * 批次匯出多張表
     *
     * @param array<string, array{
     *     modelClass?: class-string<Model>,
     *     query?: Closure(): \Illuminate\Support\Collection,
     *     tableSchema: Closure(Blueprint): void,
     *     transformer: Closure(Model): array,
     *     chunkSize?: int
     * }> $tables
     */
    public function exportMultiple(array $tables): void
    {
        foreach ($tables as $tableName => $config) {
            $this->dropAndCreateTable($tableName, $config['tableSchema']);
            $transformer = $config['transformer'] ?? fn($row) => (array)$row;

            if (isset($config['modelClass'])) {
                $modelClass = $config['modelClass'];
                $chunkSize = $config['chunkSize'] ?? 1000;
                $modelInstance = new $modelClass;

                $modelInstance->newQuery()->chunk($chunkSize, function (Collection $chunk) use ($tableName, $transformer) {
                    $rows = $chunk->map($transformer)->toArray();
                    $this->insertData($tableName, $rows);
                });
            } elseif (isset($config['query']) && is_callable($config['query'])) {
                $rows = call_user_func($config['query']);

                if ($rows instanceof \Illuminate\Database\Query\Builder || $rows instanceof \Illuminate\Database\Eloquent\Builder) {
                    $rows = $rows->get();
                }

                $data = $rows->map($transformer)->toArray();
                $this->insertData($tableName, $data);
            } else {
                throw new \InvalidArgumentException("Each table config must define either 'modelClass' or 'query'.");
            }
        }
        $this->purge();
    }

    protected function dropAndCreateTable(string $table, Closure $schema): void
    {
        Schema::connection($this->connection)->dropIfExists($table);
        Schema::connection($this->connection)->create($table, $schema);
    }

    protected function insertData(string $table, array $rows): void
    {
        if (empty($rows)) return;
        $columns = count($rows[0]);
        $maxBatch = intdiv(999, $columns);

        foreach (array_chunk($rows, $maxBatch) as $chunk) {
            DB::connection($this->connection)->table($table)->insert($chunk);
        }
    }

    protected function purge(): void
    {
        DB::purge($this->connection);
    }

}