<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

/**
 * Karmaşık kayıt kodlarını (SAL-XXXXXX-ymd, TD20260801-0421, DAY001000 …)
 * kaldırıp her modülde acente bazında 1'den başlayan düz sıra numaralarına geçirir.
 *
 *  - customers / suppliers / resources : yeni "code" kolonu + acente bazlı 1..N
 *  - sales                             : sale_code artık acente bazlı 1..N (unique: agency_id + sale_code)
 *  - products + tur tabloları          : code acente & ürün tipi bazlı 1..N (unique: agency_id + product_type_id + code)
 *  - tur tarih tabloları               : code tur bazlı 1..N
 *
 * Mevcut kayıtlar oluşturulma (id) sırasına göre yeniden numaralandırılır;
 * soft-delete edilmiş satırlar da numara alır (unique indekste yer tuttukları için).
 */
return new class extends Migration
{
    public function up(): void
    {
        // ── 1) customers / suppliers / resources: code kolonu + acente bazlı sıra ──
        foreach (['customers', 'suppliers', 'resources'] as $tableName) {
            if (!Schema::hasColumn($tableName, 'code')) {
                Schema::table($tableName, function (Blueprint $table) {
                    $table->unsignedInteger('code')->nullable()->after('id');
                });
            }

            $this->renumberByGroup($tableName, 'code', ['agency_id']);

            $this->addUniqueIfMissing($tableName, ['agency_id', 'code'], $tableName . '_agency_code_unique');
        }

        // ── 2) sales: global unique kalkar, acente bazlı sıra gelir ──
        $this->dropIndexIfExists('sales', 'sales_sale_code_unique');

        $this->renumberByGroup('sales', 'sale_code', ['agency_id']);

        $this->addUniqueIfMissing('sales', ['agency_id', 'sale_code'], 'sales_agency_sale_code_unique');

        // ── 3) products: global unique kalkar; acente + ürün tipi bazlı sıra,
        //      kodlar tur tablolarına (cultural_tours / international_tours / day_tours …) senkronlanır ──
        $this->dropIndexIfExists('products', 'products_code_unique');

        $this->renumberProductsAndSyncTourTables();

        $this->addUniqueIfMissing('products', ['agency_id', 'product_type_id', 'code'], 'products_agency_type_code_unique');

        // ── 4) tur tarihleri: tur bazlı 1..N ──
        $this->renumberByGroup('cultural_tour_dates', 'code', ['cultural_tour_id'], 'start_date');
        $this->renumberByGroup('international_tour_dates', 'code', ['international_tour_id'], 'start_date');

        if (Schema::hasTable('day_trip_tour_dates') && Schema::hasColumn('day_trip_tour_dates', 'code')) {
            $this->renumberByGroup('day_trip_tour_dates', 'code', ['day_trip_tour_id'], 'tour_date');
        }
    }

    public function down(): void
    {
        // Eski karmaşık kodlar geri üretilemez; yalnızca yapısal değişiklikler geri alınır.
        $this->dropIndexIfExists('products', 'products_agency_type_code_unique');
        $this->dropIndexIfExists('sales', 'sales_agency_sale_code_unique');

        foreach (['customers', 'suppliers', 'resources'] as $tableName) {
            $this->dropIndexIfExists($tableName, $tableName . '_agency_code_unique');
            if (Schema::hasColumn($tableName, 'code')) {
                Schema::table($tableName, function (Blueprint $table) {
                    $table->dropColumn('code');
                });
            }
        }
    }

    /** İndeks mevcutsa düşürür (tekrar çalıştırılabilir migration için). */
    private function dropIndexIfExists(string $tableName, string $indexName): void
    {
        $exists = DB::table('information_schema.statistics')
            ->whereRaw('table_schema = DATABASE()')
            ->where('table_name', $tableName)
            ->where('index_name', $indexName)
            ->exists();

        if ($exists) {
            Schema::table($tableName, function (Blueprint $table) use ($indexName) {
                $table->dropIndex($indexName);
            });
        }
    }

    /** İndeks yoksa unique olarak ekler (tekrar çalıştırılabilir migration için). */
    private function addUniqueIfMissing(string $tableName, array $columns, string $indexName): void
    {
        $exists = DB::table('information_schema.statistics')
            ->whereRaw('table_schema = DATABASE()')
            ->where('table_name', $tableName)
            ->where('index_name', $indexName)
            ->exists();

        if (!$exists) {
            Schema::table($tableName, function (Blueprint $table) use ($columns, $indexName) {
                $table->unique($columns, $indexName);
            });
        }
    }

    /**
     * Bir tablodaki kod kolonunu, verilen grup kolonları kapsamında 1'den
     * başlayarak yeniden numaralandırır. Unique çakışmalarını önlemek için
     * önce tüm satırlara geçici değer yazılır (iki fazlı güncelleme).
     *
     * @param string[] $groupColumns Sıranın sıfırlandığı kapsam (ör. agency_id)
     * @param string|null $orderColumn id'den önce uygulanacak sıralama kolonu
     */
    private function renumberByGroup(string $tableName, string $codeColumn, array $groupColumns, ?string $orderColumn = null): void
    {
        $isStringColumn = in_array(
            Schema::getColumnType($tableName, $codeColumn),
            ['string', 'varchar', 'text', 'char'],
            true
        );

        // Faz 1: unique indeks varken numaralar çakışmasın diye geçici değer
        if ($isStringColumn) {
            DB::table($tableName)->update([$codeColumn => DB::raw("CONCAT('tmp#', id)")]);
        } else {
            DB::table($tableName)->update([$codeColumn => null]);
        }

        // Faz 2: grup içinde oluşturulma sırasına göre 1..N
        $query = DB::table($tableName)->select(array_merge(['id'], $groupColumns));
        foreach ($groupColumns as $col) {
            $query->orderBy($col);
        }
        if ($orderColumn) {
            $query->orderBy($orderColumn);
        }
        $query->orderBy('id');

        $sequences = [];
        foreach ($query->get() as $row) {
            $key = implode(':', array_map(fn ($col) => $row->{$col} ?? 'null', $groupColumns));
            $sequences[$key] = ($sequences[$key] ?? 0) + 1;

            DB::table($tableName)->where('id', $row->id)->update([
                $codeColumn => $isStringColumn ? (string) $sequences[$key] : $sequences[$key],
            ]);
        }
    }

    /**
     * products tablosunu acente + ürün tipi bazında 1..N numaralandırır ve yeni
     * kodu ilgili tur tablosundaki (productable) satıra da yazar. Numara otoritesi
     * products'tır; böylece aynı tipe hizmet eden eski/yeni tablolar (ör. day_tours
     * ve day_trip_tours) arasında çakışma oluşamaz.
     */
    private function renumberProductsAndSyncTourTables(): void
    {
        $tableMap = [
            'App\\Models\\Tour\\Tour' => 'cultural_tours',
            'App\\Models\\InternationalTour\\InternationalTour' => 'international_tours',
            'App\\Models\\DayTour\\DayTour' => 'day_tours',
            'App\\Models\\Product\\DayTripTour' => 'day_trip_tours',
        ];

        $rows = DB::table('products')
            ->select('id', 'agency_id', 'product_type_id', 'productable_type', 'productable_id')
            ->orderBy('agency_id')
            ->orderBy('product_type_id')
            ->orderBy('id')
            ->get();

        $sequences = [];
        foreach ($rows as $row) {
            $key = $row->agency_id . ':' . $row->product_type_id;
            $sequences[$key] = ($sequences[$key] ?? 0) + 1;
            $code = (string) $sequences[$key];

            DB::table('products')->where('id', $row->id)->update(['code' => $code]);

            $tourTable = $tableMap[ltrim((string) $row->productable_type, '\\')] ?? null;
            if ($tourTable && Schema::hasTable($tourTable) && Schema::hasColumn($tourTable, 'code')) {
                DB::table($tourTable)->where('id', $row->productable_id)->update(['code' => $code]);
            }
        }
    }
};
