<?php

namespace App\Console\Commands;

use App\Models\EmailRecipient;
use App\Models\Octoparse\OctoparseImportLog;
use App\Models\Octoparse\OctoparseRecord;
use App\Notifications\OctoparseImportReport;
use App\Services\Octoparse\OctoparseApiClient;
use App\Services\Octoparse\OctoparseImportService;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;

class OctoparseImport extends Command
{
    protected $signature = 'octoparse:import
                            {--task= : Import only this Octoparse task ID (defaults to all enabled tasks)}
                            {--dry-run : Fetch and report without writing to the database}
                            {--manual : Mark the run as manually triggered (used by the admin UI)}
                            {--email-report= : Comma-separated emails to send a summary report to}';

    protected $description = 'Pull data from Octoparse Cloud tasks via Open API and upsert it into octoparse_records.';

    /** @var OctoparseImportService */
    private $importService;

    public function __construct(OctoparseImportService $importService)
    {
        parent::__construct();
        $this->importService = $importService;
    }

    public function handle(): int
    {
        $dryRun = (bool) $this->option('dry-run');
        $manual = (bool) $this->option('manual');
        $only = $this->option('task');

        echo "\n▶ 1 — Command entry — options parsed\n";
        // dd(['dryRun' => $dryRun, 'manual' => $manual, 'only' => $only]);

        $this->info('Octoparse Import — starting' . ($dryRun ? ' (dry-run)' : ''));

        if (!OctoparseApiClient::isAvailable()) {
            $this->error('Octoparse API credentials are not configured (OCTOPARSE_API_USERNAME / OCTOPARSE_API_PASSWORD).');
            return self::FAILURE;
        }

        echo "▶ 2 — API credentials check passed\n";
        // dd([
        //     'username' => config('octoparse.api.username'),
        //     'base_url' => config('octoparse.api.base_url'),
        //     'use_not_exported' => config('octoparse.import.use_not_exported_endpoint'),
        // ]);

        $tasks = config('octoparse.tasks', []);

        echo "▶ 3 — Tasks config loaded (" . count($tasks) . " total)\n";
        // dd($tasks);

        if (empty($tasks)) {
            $this->warn('No tasks configured under config/octoparse.php. Nothing to do.');
            return self::SUCCESS;
        }

        $selected = $this->selectTasks($tasks, $only);

        echo "▶ 4 — Tasks selected after filter (" . count($selected) . ")\n";
        // dd(['only' => $only, 'selected' => $selected]);

        if (empty($selected)) {
            $this->warn('No enabled Octoparse tasks matched the current filter.');
            return self::SUCCESS;
        }

        $summary = [];
        $hadFailure = false;

        foreach ($selected as $taskId => $taskConfig) {
            $label = $taskConfig['label'] ?? $taskId;
            $this->line("→ Importing task '{$label}' ({$taskId})");

            try {
                $log = $this->importService->importTask($taskId, $taskConfig, $dryRun, $manual);
                $summary[] = $this->summarizeLog($log, $label);

                echo "▶ 14 — Summary entry appended for task {$taskId}\n";
                // dd(end($summary));

                $this->info(sprintf(
                    '   status=%s fetched=%d inserted=%d updated=%d failed=%d',
                    $log->status,
                    $log->rows_fetched,
                    $log->rows_inserted,
                    $log->rows_updated,
                    $log->rows_failed
                ));
            } catch (\Throwable $e) {
                $hadFailure = true;
                $summary[] = [
                    'task_id' => $taskId,
                    'label' => $label,
                    'status' => 'failed',
                    'error' => $e->getMessage(),
                ];
                $this->error("   ✗ Task {$taskId} failed: " . $e->getMessage());
                Log::error("[Octoparse] Command-level failure for task {$taskId}", [
                    'error' => $e->getMessage(),
                ]);
            }
        }

        $this->sendEmailReportIfConfigured($summary, $hadFailure, $dryRun);

        return $hadFailure ? self::FAILURE : self::SUCCESS;
    }

    /**
     * @param  array<string,array<string,mixed>>  $tasks
     * @return array<string,array<string,mixed>>
     */
    private function selectTasks(array $tasks, ?string $only): array
    {
        $selected = [];

        foreach ($tasks as $taskId => $taskConfig) {
            if ($only !== null && $only !== '' && $taskId !== $only) {
                continue;
            }
            if (!($taskConfig['enabled'] ?? false)) {
                continue;
            }
            $selected[$taskId] = $taskConfig;
        }

        return $selected;
    }

    private function summarizeLog(OctoparseImportLog $log, string $label): array
    {
        return [
            'log_id' => $log->id,
            'task_id' => $log->task_id,
            'label' => $label,
            'status' => $log->status,
            'rows_fetched' => $log->rows_fetched,
            'rows_inserted' => $log->rows_inserted,
            'rows_updated' => $log->rows_updated,
            'rows_failed' => $log->rows_failed,
            'new_record_ids' => is_array($log->new_record_ids) ? $log->new_record_ids : [],
            'duration_seconds' => $log->duration_seconds,
        ];
    }

    private function sendEmailReportIfConfigured(array $summary, bool $hadFailure, bool $dryRun): void
    {
        $recipientsCsv = (string) $this->option('email-report');
        if ($recipientsCsv === '') {
            $recipientsCsv = (string) config('octoparse.email_report.recipients', '');
        }

        if ($recipientsCsv === '') {
            return;
        }

        if (config('octoparse.email_report.only_on_error') && !$hadFailure) {
            return;
        }

        $recipients = array_values(array_filter(array_map('trim', explode(',', $recipientsCsv))));
        if (empty($recipients)) {
            return;
        }

        echo "▶ 15 — Email report — recipients=" . implode(',', $recipients) . " subject prefix=" . ($hadFailure ? 'ERROR' : 'OK') . "\n";

        $sampleNewEntriesByTask = $this->buildSampleNewEntries($summary);
        $latestLogId = $this->resolveLatestLogId($summary);

        try {
            foreach ($recipients as $email) {
                $recipient = new EmailRecipient($email, 'Octoparse Admin');
                $recipient->notify(new OctoparseImportReport(
                    $summary,
                    $sampleNewEntriesByTask,
                    $dryRun,
                    $hadFailure,
                    $latestLogId
                ));
            }
            $this->info('Email report sent to: ' . implode(', ', $recipients));
        } catch (\Throwable $e) {
            $this->error('Failed to send email report: ' . $e->getMessage());
            Log::error('[Octoparse] Failed to send email report: ' . $e->getMessage());
        }
    }

    /**
     * For each task with insertions, build a short list of display strings
     * (max 10) describing the newly-inserted records.
     *
     * @param  array<int,array<string,mixed>>  $summary
     * @return array<string,array<int,string>>
     */
    private function buildSampleNewEntries(array $summary): array
    {
        $sampleByTask = [];

        foreach ($summary as $row) {
            $taskId = $row['task_id'] ?? null;
            $ids = $row['new_record_ids'] ?? [];

            if (!$taskId || !is_array($ids) || empty($ids)) {
                continue;
            }

            $sampleIds = array_slice($ids, 0, 10);
            $records = OctoparseRecord::whereIn('id', $sampleIds)->get();

            $sampleByTask[$taskId] = $records
                ->map(fn ($record) => $this->describeRecord($record))
                ->all();
        }

        return $sampleByTask;
    }

    private function describeRecord(OctoparseRecord $record): string
    {
        $key = trim((string) $record->external_key);
        if ($key !== '' && !str_starts_with($key, 'payload:')) {
            return $key;
        }

        $payload = is_array($record->payload) ? $record->payload : [];
        foreach ($payload as $field => $value) {
            if (is_scalar($value)) {
                $stringValue = trim((string) $value);
                if ($stringValue !== '') {
                    return "{$field}: {$stringValue}";
                }
            }
        }

        return "Record #{$record->id}";
    }

    /**
     * Pick a log id to link to in the email — prefer the latest successful
     * (or partial) run, else fall back to the highest log id.
     *
     * @param  array<int,array<string,mixed>>  $summary
     */
    private function resolveLatestLogId(array $summary): ?int
    {
        $ids = array_filter(array_map(
            fn ($row) => isset($row['log_id']) ? (int) $row['log_id'] : null,
            $summary
        ));

        if (empty($ids)) {
            return null;
        }

        return max($ids);
    }
}
