<?php

namespace App\Console\Commands\Migrations;

use App\Models\ProjectCharter;
use App\Models\ProjectCharterDeliverable;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Spatie\Activitylog\Models\Activity;

class ProjectCharterDeliverableMigration extends BaseMigration
{
    protected string $oldTableName = 'sek_completion_reports'; // Primary table

    protected string $modelName = 'Project Charter Deliverables';

    public function migrate(bool $dryRun = false, int $batchSize = 1000, int $startFrom = 0): void
    {
        // Migrate combined completion reports and payments as single deliverables
        $this->migrateCombinedDeliverables($dryRun, $batchSize, $startFrom);
    }

    /**
     * Create a mapping between completion reports and payments for each charter
     */
    private function createReportPaymentMapping(): array
    {
        $oldDb = DB::connection($this->connection);

        // Get all charters that have both completion reports and payments
        $charterIds = $oldDb->table('sek_completion_reports')
            ->whereNotNull('charter_id')
            ->where('report_type', 'PAID')
            ->pluck('charter_id')
            ->unique()
            ->filter(function ($charterId) use ($oldDb) {
                // Only include charters that also have payments
                return $oldDb->table('sek_payments')
                    ->where('charter_id', $charterId)
                    ->exists();
            });

        $mapping = [];

        foreach ($charterIds as $charterId) {
            // Get completion reports ordered by creation time
            $reports = $oldDb->table('sek_completion_reports')
                ->where('charter_id', $charterId)
                ->where('report_type', 'PAID')
                ->orderBy('created_at')
                ->orderBy('id')
                ->get();

            // Get payments ordered by payment number and creation time
            $payments = $oldDb->table('sek_payments')
                ->where('charter_id', $charterId)
                ->orderBy('payment_number')
                ->orderBy('created_at')
                ->get();

            // Map reports to payments by their sequence (1:1 mapping)
            for ($i = 0; $i < min(count($reports), count($payments)); $i++) {
                $mapping[] = [
                    'report' => $reports[$i],
                    'payment' => $payments[$i],
                    'charter_id' => $charterId,
                ];
            }

            // Handle any orphaned reports (reports without corresponding payments)
            for ($i = count($payments); $i < count($reports); $i++) {
                $mapping[] = [
                    'report' => $reports[$i],
                    'payment' => null,
                    'charter_id' => $charterId,
                ];
            }

            // Handle any orphaned payments (payments without corresponding reports)
            for ($i = count($reports); $i < count($payments); $i++) {
                $mapping[] = [
                    'report' => null,
                    'payment' => $payments[$i],
                    'charter_id' => $charterId,
                ];
            }
        }

        return $mapping;
    }

    private function migrateCombinedDeliverables(bool $dryRun, int $batchSize, int $startFrom): void
    {
        // Create the mapping between reports and payments
        $this->info('📊 Creating mapping between completion reports and payments...');
        $mapping = $this->createReportPaymentMapping();

        $totalRecords = count($mapping);
        $this->info("📊 Found {$totalRecords} combined deliverables to migrate");

        if ($totalRecords === 0) {
            $this->info('✅ No deliverables to migrate');

            return;
        }

        $bar = $this->command->getOutput()->createProgressBar($totalRecords);
        $bar->start();

        // Process in chunks
        $chunks = array_chunk($mapping, $batchSize);
        $processedCount = 0;

        foreach ($chunks as $chunk) {
            // Skip chunks until we reach startFrom
            if ($processedCount < $startFrom) {
                $processedCount += count($chunk);
                $bar->advance(count($chunk));

                continue;
            }

            foreach ($chunk as $item) {
                $bar->advance();

                $data = $this->mapCombinedData($item);

                if ($this->validateData($data)) {
                    try {
                        if (! $dryRun) {
                            $this->createCombinedModel($data, $item);
                        }
                        $this->stats['migrated']++;
                    } catch (\Exception $e) {
                        $this->stats['errors']++;
                        $reportId = $item['report']->id ?? 'N/A';
                        $paymentId = $item['payment']->id ?? 'N/A';
                        $this->error("❌ Error creating combined deliverable (Report: {$reportId}, Payment: {$paymentId}): {$e->getMessage()}");
                    }
                } else {
                    $this->stats['skipped']++;
                    $reportId = $item['report']->id ?? 'N/A';
                    $paymentId = $item['payment']->id ?? 'N/A';
                    $this->warn("⚠️  Skipped combined deliverable (Report: {$reportId}, Payment: {$paymentId}) - validation failed");
                }
            }
        }

        $bar->finish();
        $this->info('');
    }

    private function mapCombinedData(array $item): array
    {
        $report = $item['report'];
        $payment = $item['payment'];
        $charterId = $item['charter_id'];

        // Find the corresponding project charter
        $projectCharter = ProjectCharter::find($charterId);

        if (! $projectCharter) {
            return [];
        }

        // Combine data from both report and payment
        $name = $report ? $report->report_name : "Payment #{$payment->payment_number}";

        // Use payment description if available, otherwise use report name
        $description = $payment && $payment->payment_description
            ? $payment->payment_description
            : ($report ? $report->report_name : "Payment of {$payment->payment_value}");

        // Determine file status from report if available
        $fileStatus = null;
        if ($report) {
            $fileStatus = match ($report->report_status) {
                'PENDING' => 'pending',
                'APPROVED' => 'approved',
                'REJECTED' => 'rejected',
                default => null
            };
        }

        $estimated_cost = $payment ? $payment->payment_value : 0;

        // Use the earliest creation date
        $createdAt = $report && $payment
            ? min($report->created_at, $payment->created_at)
            : ($report ? $report->created_at : $payment->created_at);

        // Use the latest modification date
        $updatedAt = null;
        if ($report && $payment) {
            $reportUpdated = $report->modified_at ?? $report->created_at;
            $paymentUpdated = $payment->modified_at ?? $payment->created_at;
            $updatedAt = max($reportUpdated, $paymentUpdated);
        } else {
            $updatedAt = $report
                ? ($report->modified_at ?? $report->created_at)
                : ($payment->modified_at ?? $payment->created_at);
        }

        return [
            'project_charter_id' => $projectCharter->getKey(),
            'name' => $name,
            'description' => $description,
            'expected_delivery_date' => $report ? $this->parseDate($report->expected_delivery_date) : null,
            'file_status' => $fileStatus,
            'weight' => 50.0,
            'estimated_cost' => $estimated_cost,
            'rejection_reason' => null,
            'payment_number' => $payment ? (string) $payment->payment_number : null,
            'created_at' => $this->parseDateTime($createdAt),
            'updated_at' => $this->parseDateTime($updatedAt),
            'original_report' => $report,
            'original_payment' => $payment,
        ];
    }

    private function createCombinedModel(array $data, array $item): void
    {
        $report = $data['original_report'];
        $payment = $data['original_payment'];
        unset($data['original_report'], $data['original_payment']);

        // Create the deliverable with unguarded mass assignment
        ProjectCharterDeliverable::unguarded(function () use ($data, $report, $payment) {
            activity()->disableLogging();

            $deliverable = ProjectCharterDeliverable::create($data);

            activity()->enableLogging();

            // Handle approval workflow if approved (only from report data)
            if ($report && $data['file_status'] === 'approved' && $report->approved_by_admin_id && $report->approved_by_admin_datetime) {
                $approver = User::where('old_user_id', $report->approved_by_admin_id)->first();

                if ($approver) {
                    $deliverable->update([
                        'approved_at' => $this->parseDateTime($report->approved_by_admin_datetime),
                        'approved_by' => $approver->getKey(),
                    ]);

                    // Create activity log for approval
                    Activity::create([
                        'log_name' => 'default',
                        'description' => 'approved',
                        'subject_type' => ProjectCharterDeliverable::class,
                        'subject_id' => $deliverable->getKey(),
                        'causer_type' => User::class,
                        'causer_id' => $approver->getKey(),
                        'properties' => [
                            'attributes' => [
                                'file_status' => 'approved',
                                'approved_at' => $report->approved_by_admin_datetime,
                                'approved_by' => $approver->getKey(),
                            ],
                            'old' => [
                                'file_status' => 'pending',
                            ],
                        ],
                        'created_at' => $this->parseDateTime($report->approved_by_admin_datetime),
                        'updated_at' => $this->parseDateTime($report->approved_by_admin_datetime),
                    ]);
                }
            }

            $reportInfo = $report ? "Report: {$report->report_name}" : 'No report';
            $paymentInfo = $payment ? "Payment #{$payment->payment_number}" : 'No payment';
            $this->info("📄 Migrated combined deliverable ID {$deliverable->getKey()}: {$deliverable->name} ({$reportInfo} + {$paymentInfo})");
        });
    }

    protected function getModelName(): string
    {
        return 'Project Charter Deliverable';
    }

    protected function getModelClass(): string
    {
        return 'App\\Models\\ProjectCharterDeliverable';
    }

    protected function mapData(object $oldRecord): array
    {
        // This method is required by BaseMigration but not used in our combined approach
        // It's kept for compatibility but returns empty array
        return [];
    }

    protected function validateData(array $data): bool
    {
        return ! empty($data) && ! empty($data['project_charter_id']) && ! empty($data['name']);
    }

    protected function createModel(array $data): void
    {
        // This method is required by BaseMigration but not used in our combined approach
        // The actual model creation is handled by createCombinedModel
    }
}
