<?php

use App\Models\StockManagement\ImportationType;
use App\Models\StockManagement\Output;
use App\Models\StockManagement\WareHouseType;
use Illuminate\Database\Migrations\Migration;
use App\Helpers\DatabaseSchema\Schema;
use App\Helpers\DatabaseSchema\CustomBluePrint as Blueprint;

return new class extends Migration {

    public function up(){
        $tables = config("stocks-management.tables");

       // DB::beginTransaction();


        //[Table] - Warehouse
        $this->createWarehouseTable($tables);

        //[Table] - Stock
        $this->createStockTable($tables);

        //[Table] - Adjustment
        $this->createAdjustmentTable($tables);

        //[Table] - StockRequest
      //  $this->createDemandTable($tables);

        //[Table] - Importation
        $this->createImportationTable($tables);

        //[Table] - WarehouseHasManager
        $this->createWarehousHasManagerTable($tables);

        //[Table] - WarehouseHasCity
      //  $this->createWarehousHasCityTable($tables);

        //[Table] - OutPuts
      //  $this->createOutputTable($tables);

        //[AddingColumns]
        $this->addCreatedByUpdatedByColumns($tables);


        $this->addFieldsToCitiesTable($tables);


    }
    protected function createWarehouseTable($tables){
        if (array_key_exists("warehouse",$tables) and !empty($tables['warehouse'])){
            $warehouse_table = (new $tables['warehouse'])->getTable();
            if (!Schema::hasTable($warehouse_table)) {

                Schema::create($warehouse_table, function (Blueprint $table) use($tables) {
                        $table->id();

                    $table->string('code')->nullable()->unique()
                        ->comment("Le code unique ");
                        $table->string('slug')->nullable()->unique()
                            ->comment("Le slug ");

                        $table->string('name', 100)->nullable()
                            ->comment("Le nom de l'entrepôt");

                        $table->foreignIdFor($tables['city'],'city_id')->nullable()
                            ->constrained((new $tables['city'])->getTable(), (new $tables['city'])->getKeyName(), uniqid("FK_"))
                            ->cascadeOnUpdate()->cascadeOnDelete()
                            ->comment("[FK] La ville associée (Ex: '1')");

                        //ajout de la colonne 'store_id' pour gerer le cas des "points de ventes"
                        $table->unsignedBigInteger("store_id")->nullable()->index()
                                ->comment("[FK] le magasin qui l'approvisionne (Ex: '2')");

                        $table->string(WareHouseType::FK_ID,60)->nullable()->index()
                            ->comment("le type d'entrepot");

                        $table->boolean('is_bad_goods')->default(false)
                            ->comment("Détermine si l'entrepot stockera des produits déffectueux");

                        $table->timestamps();
                        $table->softDeletes();


                    $table->foreign('store_id')
                        ->references((new $tables['warehouse'])->getKeyName())
                        ->on((new $tables['warehouse'])->getTable())
                        ->onUpdate('cascade')
                        ->onDelete('cascade');
                    });
            }

        }
    }

    /** Cree la table "stock"
     * @param $tables
     */
    protected function createStockTable($tables){
        if (array_key_exists("stock",$tables) and !empty($tables['stock'])){
            $table_name = (new $tables['stock'])->getTable();

            if (!Schema::hasTable($table_name))
                Schema::create($table_name, function (Blueprint $table) use($tables) {
                    $table->id();

                    $table->string('lot_id', 60)
                        ->comment("L'id du lot (Ex: 'LOTM1')");

                    $table->foreignIdFor($tables['product'],'product_id')
                        ->constrained((new $tables['product'])->getTable(), (new $tables['product'])->getKeyName(), uniqid("FK_"))
                        ->cascadeOnUpdate()->cascadeOnDelete()
                        ->comment("[FK] L'entrepot associé (Ex: '1')");

                    $table->float('qty')->nullable()
                          ->comment("La quantité en stock (Ex: '1')");

                    $table->float('reserved_qty')->nullable()
                        ->comment("La quantité reservé en stock (Ex: '5')");
                    $table->date('expired_at')->nullable()
                          ->comment("La date d'expiration du lot (Ex: '2022-05-21')");

                    $table->foreignIdFor($tables['warehouse'],'warehouse_id')
                        ->constrained((new $tables['warehouse'])->getTable(), (new $tables['warehouse'])->getKeyName(), uniqid("FK_"))
                        ->cascadeOnUpdate()->cascadeOnDelete()
                        ->comment("[FK] L'entrepot associé (Ex: '1')");

                    $table->timestamps();
                    $table->softDeletes();

                    if (array_key_exists("product",$tables) and array_key_exists("warehouse",$tables)){
                        $table->unique(['lot_id', "product_id" , "warehouse_id",]);
                    }
                });
        }
    }

    protected function createImportationTable($tables){
        if (array_key_exists("stock-importation",$tables) and !empty($tables['stock-importation'])){
            $table_name = (new $tables['stock-importation'])->getTable();

            if (!Schema::hasTable($table_name))
                Schema::create($table_name, function (Blueprint $table) use($tables) {
                    $table->id();
                    $table->string('lot_id', 60)
                          ->comment("L'id du lot (Ex: 'LOTM1')");
                    $table->foreignIdFor($tables['product'],'product_id')
                        ->constrained((new $tables['product'])->getTable(), (new $tables['product'])->getKeyName(), uniqid("FK_"))
                        ->cascadeOnUpdate()->cascadeOnDelete()
                        ->comment("[FK] L'entrepot associé (Ex: '1')");

                    $table->float('qty')->nullable()
                        ->comment("La quantité en stock lors de l'ajustement (Ex: '1')");

                    $table->string('default_comment')->nullable()
                        ->comment("Le commentaire par défaut lors de l'importation (Ex: '5')");

                    $table->string(ImportationType::FK_ID,100)->nullable()->index()
                        ->comment("[FKI] le type d'importation");

                    $table->date('expired_at')->nullable()
                          ->comment("La date de péremtion du lot (Ex: '1')");

                    $table->foreignIdFor($tables['stock'],'stock_id')
                        ->constrained((new $tables['stock'])->getTable(), (new $tables['stock'])->getKeyName(), uniqid("FK_"))
                        ->cascadeOnUpdate()->cascadeOnDelete()
                        ->comment("[FK] Le stock associé (Ex: '1')");

                    $table->foreignIdFor($tables['warehouse'],'warehouse_id')->nullable()
                        ->constrained((new $tables['warehouse'])->getTable(), (new $tables['warehouse'])->getKeyName(), uniqid("FK_"))
                        ->cascadeOnUpdate()->cascadeOnDelete()
                        ->comment("[FK] L'entrepot associé (Ex: '1')");

                    $table->foreignIdFor($tables['user'],'created_by')->nullable()
                        ->constrained((new $tables['user'])->getTable(), (new $tables['user'])->getKeyName(), uniqid("FK_"))
                        ->cascadeOnUpdate()->cascadeOnDelete()
                        ->comment("[FK] L'auteur de l'enregistrement (Ex: '1')");

                    $table->timestamp("created_at");
                    $table->softDeletes();

                });
        }
    }

    protected function createDemandTable($tables){
        if (array_key_exists("stock-request",$tables) and !empty($tables['stock-request'])){
            $request_table_name = (new $tables['stock-request'])->getTable();

            if (!Schema::hasTable($request_table_name)) {
                Schema::create($request_table_name, function (Blueprint $table) use($tables) {
                    $table->id();
                    $table->string('code', 100)->unique(uniqid("UQ_"))
                        ->comment("Le code unique");

                    $table->foreignIdFor($tables['warehouse'],'requester_warehouse_id')
                        ->constrained((new $tables['warehouse'])->getTable(), (new $tables['warehouse'])->getKeyName(), uniqid("FK_"))
                        ->cascadeOnUpdate()->cascadeOnDelete()
                        ->comment("[FK] L'entrepot demandeur associé (Ex: '1')");

                    $table->foreignIdFor($tables['warehouse'],'receiver_warehouse_id')
                        ->constrained((new $tables['warehouse'])->getTable(), (new $tables['warehouse'])->getKeyName(), uniqid("FK_"))
                        ->cascadeOnUpdate()->cascadeOnDelete()
                        ->comment("[FK] L'entrepot recepeteur associé (Ex: '1')");

//                    $table->unsignedBigInteger(ImportationType::FK_ID)
//                        ->nullable()
//                        ->index()
//                        ->comment("[FKI] le type d'importation (Ex: '2')");
                });
            }
            $request_items_table_name = (new $tables['stock-request-item'])->getTable();
            if (!Schema::hasTable($request_items_table_name))
                Schema::create($request_items_table_name, function (Blueprint $table) use($tables) {
                    $table->id();
                    $table->string('lot_id', 60)
                        ->comment("L'id du lot (Ex: 'LOTM1')");

                    $table->foreignIdFor($tables['stock'],'stock_id')
                        ->constrained((new $tables['stock'])->getTable(), (new $tables['stock'])->getKeyName(), uniqid("FK_"))
                        ->cascadeOnUpdate()->cascadeOnDelete()
                        ->comment("[FK] Le stock associé (Ex: '1')");

                    $table->float('qty')->nullable()
                        ->comment("La quantité en stock lors de l'ajustement (Ex: '1')");

                    $table->foreignIdFor($tables['stock-request'],'product_id')
                        ->constrained((new $tables['stock-request'])->getTable(), (new $tables['stock-request'])->getKeyName(), uniqid("FK_"))
                        ->cascadeOnUpdate()->cascadeOnDelete()
                        ->comment("[FK] La demande de stock (Ex: '1')");

                    $table->foreignIdFor($tables['product'],'product_id')
                        ->constrained((new $tables['product'])->getTable(), (new $tables['product'])->getKeyName(), uniqid("FK_"))
                        ->cascadeOnUpdate()->cascadeOnDelete()
                        ->comment("[FK] L'entrepot associé (Ex: '1')");

                    $table->float('transfered_qty')
                        ->nullable()
                        ->comment("La quantité transférée (lors de la reception d'une demande)");

                    $table->float('received_qty')
                        ->nullable()
                        ->comment("La quantité reçue (lors de la reception d'une demande)");

                    $table->timestamps();
                    $table->softDeletes();

                });
        }
    }

    protected function createAdjustmentTable($tables){
        if (array_key_exists("stock-adjustment",$tables) and !empty($tables['stock-adjustment'])){
            $table_name = (new $tables['stock-adjustment'])->getTable();

            if (!Schema::hasTable($table_name))

                Schema::create($table_name, function (Blueprint $table) use($tables) {
                    $table->id();

                    $table->string($tables['stock-adjustment']::MORPH_ID_COLUMN, 60)->nullable()
                        ->comment("[polymorph_id] l'id du model reponsable de l'ajustement  (ex : '1')");
                    $table->string($tables['stock-adjustment']::MORPH_TYPE_COLUMN, 100)->nullable()
                        ->comment("[polymorph_type] le type du model reponsable de l'ajustement  nom de cette table utilisé par laravel)
                  pour faire la correspondance de l'enregistrement  (ex : 'user')");

                    $table->foreignIdFor($tables['stock'],'stock_id')
                        ->constrained((new $tables['stock'])->getTable(), (new $tables['stock'])->getKeyName(), uniqid("FK_"))
                        ->cascadeOnUpdate()->cascadeOnDelete()
                        ->comment("[FK] Le stock associé");

                    $table->float('qty_before')->nullable()
                        ->comment("Qté en stock avant l'ajustement");


                    $table->float('qty_adjusted')->nullable()
                          ->comment("La quantité ajustée");

                    $table->float('qty_after')->nullable()
                        ->comment("Qté en stock après l'ajustement");

                    $table->string("reason_code",60)->nullable()->index()
                          ->comment("[FKI] Le type d'ajustement");
                    $table->string("operation_code",60)->nullable()->index()
                          ->comment("[FKI] l'opération d'ajustement");

                    $table->longText("comment")->nullable()
                           ->comment("Le commentaure rattaché à l'ajustement");

                    $table->boolean("replaced")->default(false)
                          ->comment("Determine s'il ya eu retour en stock sur cet ajustement");

                    $table->unsignedBigInteger('parent_id')->nullable()
                        ->comment("[FK] la ligne parente");

                    $table->timestamps();
                    $table->softDeletes();

                    $table->foreign('parent_id')
                        ->references((new $tables['stock-adjustment'])->getKeyName())
                        ->on((new $tables['stock-adjustment'])->getTable())
                        ->onUpdate('cascade')
                        ->onDelete('cascade');
                });
        }
    }

    protected function createOutputTable($tables){

        if (array_key_exists("stock-output",$tables) and filled($tables['stock-output'])){
            $table_name = (new $tables['stock-output'])->getTable();

            if (!Schema::hasTable($table_name))
                Schema::create($table_name, function (Blueprint $table) use($tables) {
                    $table->id();

                    $table->string(Output::MORPH_ID_COLUMN, 60)->nullable()
                        ->comment("[polymorph_id] l'id du model qui a débité le stock  (ex : '1')");
                    $table->string(Output::MORPH_TYPE_COLUMN, 100)->nullable()
                        ->comment("[polymorph_type] le type du model débiteur  nom de cette table utilisé par laravel)
                  pour faire la correspondance de l'enregistrement  (ex : 'product')");

                    $table->foreignIdFor($tables['stock'],'stock_id')
                        ->constrained((new $tables['stock'])->getTable(), (new $tables['stock'])->getKeyName(), uniqid("FK_"))
                        ->cascadeOnUpdate()->cascadeOnDelete()
                        ->comment("[FK] Le stock associé au débit (Ex: '1')");

                    $table->float('debited_qty')
                        ->nullable()
                        ->comment("La quantité débité sur le stock(lors de la soummission d'une demande ou commande d'un produit)");

                    $table->float('qty_before')
                        ->nullable()
                        ->comment("L'ancienne qté avant le débit");

                    $table->float('qty_after')
                        ->nullable()
                        ->comment("La nouvelle quantité restante après le débit");

                    $table->boolean('rejected')
                          ->default(false)
                          ->comment("Détermine si c'est rejeté");

                    $table->timestamp("created_at");

                });
        }
    }

    protected function createWarehousHasCityTable($tables){
        if (array_key_exists("warehouse-has-city",$tables) and !empty($tables['warehouse-has-city'])){
            $table_name = (new $tables['warehouse-has-city'])->getTable();
            if (!Schema::hasTable($table_name))
                Schema::create($table_name, function (Blueprint $table) use($tables) {

                    $table->foreignIdFor($tables['city'],'city_id')
                        ->constrained((new $tables['city'])->getTable(),(new $tables['city'])->getKeyName(), uniqid("FK_"))
                        ->cascadeOnUpdate()->cascadeOnDelete()
                        ->comment("[FK] La ville associée (city) (Ex: '1')");

                    $table->foreignIdFor($tables['warehouse'],"warehouse_id")
                        ->nullable()
                        ->constrained((new $tables['warehouse'])->getTable(), (new $tables['warehouse'])->getKeyName(), uniqid("FK_"))
                        ->cascadeOnUpdate()->cascadeOnDelete()
                        ->comment("[FK] L'entrepôt affecté (Ex: '1')");

                    //la combinaison des clés primaires
                    $table->primary(['city_id',"warehouse_id"]);
                });
        }
    }

    protected function createWarehousHasManagerTable($tables){

        if (array_key_exists("warehouse-has-manager",$tables) and !empty($tables['warehouse-has-manager'])){
            $table_name = (new $tables['warehouse-has-manager'])->getTable();
            if (!Schema::hasTable($table_name))
                Schema::create($table_name, function (Blueprint $table) use($tables) {
                    $table->unsignedBigInteger("id",true)->unique();

                    $table->foreignIdFor($tables['user'],'manager_id')
                        ->constrained((new $tables['user'])->getTable(),(new $tables['user'])->getKeyName(), uniqid("FK_"))
                        ->cascadeOnUpdate()->cascadeOnDelete()
                        ->comment("[FK] Le gérant (admin) (Ex: '1')");

                    $table->foreignIdFor($tables['warehouse'],"warehouse_id")
                        ->nullable()
                        ->constrained((new $tables['warehouse'])->getTable(), (new $tables['warehouse'])->getKeyName(), uniqid("FK_"))
                        ->cascadeOnUpdate()->cascadeOnDelete()
                        ->comment("[FK] L'entrepôt affecté (Ex: '1')");

                    //la combinaison des clés primaires
                   // $table->primary(['manager_id',"warehouse_id"],uniqid("UQ_"));
                });
        }
    }

    protected function addCreatedByUpdatedByColumns($tables){
        foreach ($tables as $tbl => $class) {

            if (in_array($tbl,["warehouse","stock-request","stock-adjustment","stock-importation"])){
                //ajout de la colonne 'created_by' pour stocker l'utilisateur qui a enregistré la ligne


                if (Schema::hasTable((new $tables[$tbl])->getTable())) {
                    Schema::whenTableDoesntHaveColumn( (new $tables[$tbl])->getTable(),"created_by",function (Blueprint $table) use($tbl,$tables){
                        $table->foreignIdFor(new $tables['user'],"created_by")
                            ->nullable()
                            ->constrained((new $tables['user'])->getTable(), (new $tables['user'])->getKeyName(), uniqid("FK_"))
                            ->cascadeOnUpdate()->cascadeOnDelete()
                            ->comment("[FK] user(auteur) de l'enregistrement (Ex: '2')");
                    });
                }

                if (Schema::hasTable((new $tables[$tbl])->getTable())) {
                    //ajout de la colonne 'updated_by' pour stocker le dernier utilisateur a modifié la ligne
                    Schema::whenTableDoesntHaveColumn((new $tables[$tbl])->getTable(),"updated_by",function (Blueprint $table) use($tbl,$tables){
                        $table->foreignIdFor(new $tables['user'],"updated_by")
                            ->nullable()
                            ->constrained((new $tables['user'])->getTable(), (new $tables['user'])->getKeyName(), uniqid("FK_"))
                            ->cascadeOnUpdate()->cascadeOnDelete()
                            ->comment("[FK] user(auteur) de la dernière modification (Ex: '2')");
                    });
                }
            }
        }

    }

    protected function addFieldsToCitiesTable($tables){
        Schema::whenTableDoesntHaveColumn((new $tables['city'])->getTable(), "warehouse_id",function (Blueprint $table) use($tables) {
            $table->foreignIdFor($tables['warehouse'],'warehouse_id')
                ->nullable()
                ->constrained((new $tables['warehouse'])->getTable(), (new $tables['warehouse'])->getKeyName(), uniqid("FK_"))
                ->cascadeOnUpdate()->cascadeOnDelete()
                ->comment("[FK] L'entrepot associé (Ex: '1')");;
        });
    }
    public function down(){
//        Schema::dropIfExists('stock_management_tables');
    }
};
