<?php

namespace Database\Seeders;

use App\Models\Association;
use App\Models\Department;
use App\Models\DonorFinancialPortfolio;
use App\Models\Initiative;
use App\Models\InitiativeOperationGoal;
use App\Models\InitiativeProject;
use App\Models\InitiativeResult;
use App\Models\Order;
use App\Models\OrderAgreement;
use App\Models\OrderFinance;
use App\Models\OrderRequirement;
use App\Models\PerformanceCard;
use App\Models\PerformanceCardResult;
use App\Models\PerformanceWallet;
use App\Models\ProjectCharter;
use App\Models\ProjectCharterDeliverable;
use App\Models\SekayaValue;
use App\Models\User;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\DB;

class DevelopmentSeeder extends Seeder
{
    /**
     * Run the development database seeds using factories.
     */
    public function run(): void
    {
        $this->command->info('🏭 Starting factory-based development seeding...');

        DB::transaction(function () {
            // Create additional users for testing various roles
            $this->seedDevelopmentUsers();

            // Create sample associations with orders
            $this->seedDevelopmentAssociations();

            // Create orders with full workflow states
            $this->seedDevelopmentOrders();

            // Create enhanced donor portfolios
            $this->seedDevelopmentPortfolios();

            // Create performance cards with complete hierarchies
            $this->seedDevelopmentPerformanceCards();

            // Create specific testing scenarios
            $this->seedDevelopmentScenarios();
        });

        $this->command->info('✅ Factory-based development seeding completed!');
    }

    /**
     * Create development users for testing
     */
    private function seedDevelopmentUsers(): void
    {
        $this->command->info('👥 Creating development users...');

        // Create internal staff users
        User::factory()->count(5)->create([
            'association_id' => null,
        ]);
    }

    /**
     * Create development associations with users
     */
    private function seedDevelopmentAssociations(): void
    {
        $this->command->info('🏢 Creating development associations...');

        // Create 3 additional associations using factory
        Association::factory()->count(3)->create()->each(function ($association) {
            // Create 2-4 users per association
            User::factory()->count(fake()->numberBetween(2, 4))->create([
                'association_id' => $association->id,
            ]);
        });
    }

    /**
     * Create development orders with complete workflows
     */
    private function seedDevelopmentOrders(): void
    {
        $this->command->info('📋 Creating development orders...');

        $associations = Association::all();

        foreach ($associations as $association) {
            $associationUsers = $association->admins;
            if ($associationUsers->isEmpty()) {
                continue;
            }

            // Create orders using factory with different configurations
            $this->createOrderScenarios($association, $associationUsers);
        }
    }

    /**
     * Create various order scenarios for testing
     */
    private function createOrderScenarios(Association $association, $users): void
    {
        // Scenario 1: Simple approved order
        $approvedOrder = Order::factory()->create([
            'association_id' => $association->id,
            'status' => Order::STATUS_CEO_APPROVED,
            'created_by' => $users->random()->id,
        ]);

        // Add requirements and finances
        OrderRequirement::factory()->count(3)->accepted()->create([
            'order_id' => $approvedOrder->id,
        ])->each(function ($requirement) use ($approvedOrder) {
            OrderFinance::factory()->mediumComplexity()->create([
                'order_id' => $approvedOrder->id,
                'order_requirement_id' => $requirement->id,
            ]);
        });

        OrderAgreement::factory()->create([
            'order_id' => $approvedOrder->id,
        ]);

        // Scenario 2: Order with mixed requirement statuses
        $complexOrder = Order::factory()->create([
            'association_id' => $association->id,
            'status' => Order::STATUS_TECHNICAL_FINANCIAL_REQUESTED,
            'created_by' => $users->random()->id,
        ]);

        OrderRequirement::factory()->accepted()->create(['order_id' => $complexOrder->id]);
        OrderRequirement::factory()->rejected()->create(['order_id' => $complexOrder->id]);
        OrderRequirement::factory()->needsModification()->create(['order_id' => $complexOrder->id]);
        OrderRequirement::factory()->pending()->create(['order_id' => $complexOrder->id]);

        // Scenario 3: High-value order
        $highValueOrder = Order::factory()->create([
            'association_id' => $association->id,
            'status' => Order::STATUS_LAUNCHED,
            'created_by' => $users->random()->id,
            'type' => Order::TYPE_CENTRAL_STORAGE_TANK,
        ]);

        OrderRequirement::factory()->count(5)->accepted()->create([
            'order_id' => $highValueOrder->id,
        ])->each(function ($requirement) use ($highValueOrder) {
            OrderFinance::factory()->highCost()->create([
                'order_id' => $highValueOrder->id,
                'order_requirement_id' => $requirement->id,
            ]);
        });

        OrderAgreement::factory()->highValue()->create([
            'order_id' => $highValueOrder->id,
        ]);

        // Scenario 4: Emergency order
        $emergencyOrder = Order::factory()->create([
            'association_id' => $association->id,
            'status' => Order::STATUS_AGREEMENT,
            'created_by' => $users->random()->id,
            'type' => Order::TYPE_TANKER,
        ]);

        OrderRequirement::factory()->count(2)->accepted()->create([
            'order_id' => $emergencyOrder->id,
        ])->each(function ($requirement) use ($emergencyOrder) {
            OrderFinance::factory()->lowCost()->create([
                'order_id' => $emergencyOrder->id,
                'order_requirement_id' => $requirement->id,
            ]);
        });

        OrderAgreement::factory()->emergencyContract()->create([
            'order_id' => $emergencyOrder->id,
        ]);

        // Scenario 5: Rejected order
        $rejectedOrder = Order::factory()->create([
            'association_id' => $association->id,
            'status' => Order::STATUS_REJECTED,
            'created_by' => $users->random()->id,
        ]);

        OrderRequirement::factory()->count(2)->rejected()->create([
            'order_id' => $rejectedOrder->id,
        ]);
    }

    /**
     * Create development donor portfolios
     */
    private function seedDevelopmentPortfolios(): void
    {
        $this->command->info('💰 Creating development portfolios...');

        // Create various types of portfolios
        DonorFinancialPortfolio::factory()->largeFund()->create();
        DonorFinancialPortfolio::factory()->emergencyFund()->create();
        DonorFinancialPortfolio::factory()->communityFund()->count(2)->create();
        DonorFinancialPortfolio::factory()->infrastructureFund()->create();
        DonorFinancialPortfolio::factory()->count(3)->create(); // Generic portfolios
    }

    /**
     * Create development performance cards with complete hierarchies
     */
    private function seedDevelopmentPerformanceCards(): void
    {
        $this->command->info('🎯 Creating development performance system...');

        $departments = Department::all();
        $users = User::whereNull('association_id')->get();
        $productValues = SekayaValue::where('type', SekayaValue::TYPE_PERFORMANCE_CARD_PRODUCT)->get();

        foreach ($departments as $department) {
            // Create 1-2 performance cards per department using factories
            $cardCount = fake()->numberBetween(1, 2);

            for ($i = 0; $i < $cardCount; $i++) {
                $performanceCard = PerformanceCard::factory()->create([
                    'department_id' => $department->id,
                ]);

                // Add results
                PerformanceCardResult::factory()->count(fake()->numberBetween(3, 6))->create([
                    'performance_card_id' => $performanceCard->id,
                ]);

                // Assign products and users
                if ($productValues->isNotEmpty()) {
                    $performanceCard->products()->attach(
                        $productValues->random(fake()->numberBetween(1, 3))->pluck('id')
                    );
                }

                if ($users->isNotEmpty()) {
                    $performanceCard->assignees()->attach(
                        $users->random(fake()->numberBetween(1, 3))->pluck('id')
                    );
                }

                // Create wallet
                $wallet = PerformanceWallet::factory()->create([
                    'performance_card_id' => $performanceCard->id,
                    'created_by' => $users->isNotEmpty() ? $users->random()->id : 1,
                ]);

                // Create initiatives
                $this->createDevelopmentInitiatives($wallet, $users, $productValues, $performanceCard);
            }
        }
    }

    /**
     * Create development initiatives with projects
     */
    private function createDevelopmentInitiatives(PerformanceWallet $wallet, $users, $productValues, PerformanceCard $performanceCard): void
    {
        $initiativeCount = fake()->numberBetween(1, 3);

        for ($i = 0; $i < $initiativeCount; $i++) {
            $initiative = Initiative::factory()->create([
                'performance_wallet_id' => $wallet->id,
                'created_by' => $users->isNotEmpty() ? $users->random()->id : 1,
                'start_date' => fake()->dateTimeBetween($wallet->start_date, $wallet->expected_end_date),
                'expected_end_date' => fake()->dateTimeBetween($wallet->start_date, $wallet->expected_end_date),
            ]);

            // Assign products
            if ($productValues->isNotEmpty()) {
                $initiative->products()->attach(
                    $productValues->random(fake()->numberBetween(1, 2))->pluck('id')
                );
            }

            // Create projects with proper weight distribution
            $projectCount = fake()->numberBetween(2, 4);

            // Generate weights that sum to exactly 100
            $weights = [];
            $totalWeight = 100;

            // Generate random weights for all but the last project
            for ($j = 0; $j < $projectCount - 1; $j++) {
                $remainingProjects = $projectCount - $j;
                $maxWeight = min(50, $totalWeight - (($remainingProjects - 1) * 10)); // Leave at least 10% for each remaining project
                $minWeight = max(10, $totalWeight - (($remainingProjects - 1) * 50)); // Use at least 10%

                $weight = fake()->numberBetween($minWeight, $maxWeight);
                $weights[] = $weight;
                $totalWeight -= $weight;
            }

            // Last project gets exactly the remaining weight
            $weights[] = $totalWeight;

            for ($j = 0; $j < $projectCount; $j++) {
                $project = InitiativeProject::factory()->create([
                    'initiative_id' => $initiative->id,
                    'weight' => $weights[$j],
                ]);

                // Create charter for some projects
                if (fake()->boolean(70)) {
                    $charter = ProjectCharter::factory()->create([
                        'initiative_project_id' => $project->id,
                    ]);

                    // Create deliverables
                    ProjectCharterDeliverable::factory()->count(fake()->numberBetween(2, 4))->create([
                        'project_charter_id' => $charter->id,
                    ]);
                }
            }

            // Create operation goals
            InitiativeOperationGoal::factory()->count(fake()->numberBetween(3, 6))->create([
                'initiative_id' => $initiative->id,
            ]);

            // Create results
            InitiativeResult::factory()->count(fake()->numberBetween(1, 3))->create([
                'initiative_id' => $initiative->id,
            ])->each(function ($result) use ($performanceCard) {
                // Link to performance card results
                $performanceCardResults = $performanceCard->results;
                if ($performanceCardResults->isNotEmpty()) {
                    $result->performanceCardResults()->attach(
                        $performanceCardResults->random(fake()->numberBetween(1, min(2, $performanceCardResults->count())))->pluck('id')
                    );
                }
            });
        }
    }

    /**
     * Create specific testing scenarios
     */
    private function seedDevelopmentScenarios(): void
    {
        $this->command->info('🧪 Creating development testing scenarios...');

        // Create a fully funded order scenario
        $this->createFullyFundedOrderScenario();

        // Create an over-budget project scenario
        $this->createOverBudgetProjectScenario();

        // Create a delayed initiative scenario
        $this->createDelayedInitiativeScenario();
    }

    /**
     * Create a fully funded order scenario
     */
    private function createFullyFundedOrderScenario(): void
    {
        $association = Association::first();
        $user = $association->admins->first();

        if (! $user) {
            return;
        }

        $order = Order::factory()->create([
            'association_id' => $association->id,
            'status' => Order::STATUS_LAUNCHED,
            'created_by' => $user->id,
            'type' => Order::TYPE_STATION,
        ]);

        // Create requirements and finances
        $requirements = OrderRequirement::factory()->count(4)->accepted()->create([
            'order_id' => $order->id,
        ]);

        $totalCost = 0;
        foreach ($requirements as $requirement) {
            $finance = OrderFinance::factory()->create([
                'order_id' => $order->id,
                'order_requirement_id' => $requirement->id,
            ]);
            $totalCost += $finance->offer_price + $finance->additional_fees;
        }

        // Create agreement
        $agreement = OrderAgreement::factory()->create([
            'order_id' => $order->id,
            'total_contract_value' => $totalCost,
        ]);

        // Fully fund from multiple portfolios
        $portfolios = DonorFinancialPortfolio::take(3)->get();
        $allocations = [
            $totalCost * 0.5,
            $totalCost * 0.3,
            $totalCost * 0.2,
        ];

        foreach ($portfolios as $index => $portfolio) {
            if (isset($allocations[$index])) {
                $order->donorFinancialPortfolios()->attach($portfolio->id, [
                    'allocated_amount' => $allocations[$index],
                    'created_at' => now(),
                    'updated_at' => now(),
                ]);
            }
        }
    }

    /**
     * Create an over-budget project scenario
     */
    private function createOverBudgetProjectScenario(): void
    {
        $department = Department::first();
        $users = User::whereNull('association_id')->get();

        $performanceCard = PerformanceCard::factory()->create([
            'department_id' => $department->id,
            'project_manner' => PerformanceCard::PROJECT_MANNER_INVESTMENT,
        ]);

        $wallet = PerformanceWallet::factory()->create([
            'performance_card_id' => $performanceCard->id,
            'created_by' => $users->first()->id,
        ]);

        $initiative = Initiative::factory()->create([
            'performance_wallet_id' => $wallet->id,
            'created_by' => $users->first()->id,
            'expected_cost' => 200000.00,
            'emergency_reserve' => 30000.00,
        ]);

        // Create result showing over-budget scenario
        InitiativeResult::factory()->create([
            'initiative_id' => $initiative->id,
            'expected_q1' => 50000.00,
            'actual_q1' => 65000.00, // Over budget
            'expected_q2' => 50000.00,
            'actual_q2' => 58000.00, // Over budget
            'expected_q3' => 50000.00,
            'actual_q3' => null,
            'expected_q4' => 50000.00,
            'actual_q4' => null,
        ]);
    }

    /**
     * Create a delayed initiative scenario
     */
    private function createDelayedInitiativeScenario(): void
    {
        $department = Department::skip(1)->first();
        $users = User::whereNull('association_id')->get();

        $performanceCard = PerformanceCard::factory()->create([
            'department_id' => $department->id,
            'project_manner' => PerformanceCard::PROJECT_MANNER_DEVELOPMENTAL,
        ]);

        $wallet = PerformanceWallet::factory()->create([
            'performance_card_id' => $performanceCard->id,
            'created_by' => $users->first()->id,
            'start_date' => now()->subMonths(6),
            'expected_end_date' => now()->subMonths(1), // Already passed
        ]);

        $initiative = Initiative::factory()->create([
            'performance_wallet_id' => $wallet->id,
            'created_by' => $users->first()->id,
            'start_date' => now()->subMonths(5),
            'expected_end_date' => now()->subWeeks(2), // Already passed
        ]);

        // Create projects with delayed status
        InitiativeProject::factory()->count(3)->create([
            'initiative_id' => $initiative->id,
            'weight' => 33.33,
        ])->each(function ($project) {
            // Create charter with delayed deliverables
            $charter = ProjectCharter::factory()->create([
                'initiative_project_id' => $project->id,
                'status' => 'awaiting_approval', // Still waiting
                'expected_end_date' => now()->subWeeks(1), // Should have been done
            ]);

            ProjectCharterDeliverable::factory()->count(2)->create([
                'project_charter_id' => $charter->id,
                'expected_delivery_date' => now()->subWeeks(2), // Overdue
            ]);
        });
    }
}
