<?php

namespace App\Imports;

use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;
use Maatwebsite\Excel\Concerns\ToCollection;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Mtc\MercuryDataModels\ContentMigrationEntry;
use Mtc\MercuryDataModels\Franchise;
use Mtc\MercuryDataModels\Template;

class ContentMigrationImport implements ToCollection, WithHeadingRow
{
    private Collection $franchises;
    private Collection $templates;

    private Collection $existingUrls;

    private int $imported = 0;
    private int $merged = 0;
    private int $skipped = 0;
    private array $errors = [];

    public function __construct(
        private array $mapping,
        private bool $mergeExisting = false,
    ) {
        $this->franchises = Franchise::all()->pluck('id', 'name');
        $this->templates = Template::all()->pluck('id', 'name');
        $this->existingUrls = ContentMigrationEntry::whereNotNull('original_url')->pluck('original_url');
    }

    public function collection(Collection $rows): void
    {
        foreach ($rows as $index => $row) {
            try {
                $mapped = $this->mapRow($row->toArray());

                if (empty($mapped['original_url'])) {
                    $this->skipped++;
                    continue;
                }

                if ($this->existingUrls->contains($mapped['original_url'])) {
                    if (!$this->mergeExisting) {
                        $this->skipped++;
                        continue;
                    }

                    $entry = ContentMigrationEntry::where('original_url', $mapped['original_url'])->first();
                    if (!$entry) {
                        $this->skipped++;
                        continue;
                    }

                    // Apply only fields the spreadsheet actually provided.
                    // mapRow() already drops empty source cells, so this won't blank
                    // existing data with empty cells.
                    $updates = array_diff_key($mapped, ['original_url' => true]);
                    if (!empty($updates)) {
                        $entry->fill($updates)->save();
                    }
                    $this->merged++;
                    continue;
                }

                ContentMigrationEntry::create($mapped);
                $this->existingUrls->push($mapped['original_url']);
                $this->imported++;
            } catch (\Exception $e) {
                Log::warning('Content migration import error on row ' . ($index + 2), [$e->getMessage()]);
                $this->errors[] = 'Row ' . ($index + 2) . ': ' . $e->getMessage();
            }
        }
    }

    private function mapRow(array $row): array
    {
        $data = [];
        $unmapped = [];
        $booleanFields = ['text_migrated', 'imagery_migrated', 'checked', 'metas_ready'];
        $knownFields = [
            'original_url', 'original_parent', 'original_sub', 'original_sub_sub',
            'new_url', 'franchise', 'record_type', 'template', 'status',
            'text_migrated', 'imagery_migrated', 'checked',
            'redirect_url', 'metas_ready', 'target_review_date',
            'website_section', 'priority', 'owner', 'comments',
            'pageviews', 'avg_engagement', 'key_events',
        ];
        $statusMap = ['keep' => 'keep', 'delete' => 'delete', 'update' => 'update', 'redirect' => 'redirect'];
        $priorityMap = [
            '1' => 'highest', '2' => 'high', '3' => 'medium', '4' => 'low', '5' => 'lowest',
            'highest' => 'highest', 'urgent' => 'highest', 'asap' => 'highest', 'critical' => 'highest',
            'high' => 'high',
            'medium' => 'medium', 'med' => 'medium', 'normal' => 'medium',
            'low' => 'low',
            'lowest' => 'lowest', 'minor' => 'lowest',
        ];

        foreach ($row as $header => $value) {
            $field = $this->mapping[$header] ?? null;

            if (!$field || $field === 'skip') {
                if ($value !== null && $value !== '') {
                    $unmapped[$header] = $value;
                }
                continue;
            }

            // Skip blank source cells so a sparse spreadsheet (e.g. analytics-only
            // enrichment) doesn't overwrite existing values with nulls/false on merge.
            if ($value === null || $value === '') {
                continue;
            }

            if (in_array($field, $booleanFields)) {
                $data[$field] = $this->parseBoolean($value);
            } elseif ($field === 'status') {
                // Unrecognised values stay unset so the user has to triage
                // them explicitly — same default as the DB column.
                $normalised = strtolower(trim((string) $value));
                $data[$field] = $statusMap[$normalised] ?? null;
            } elseif ($field === 'priority') {
                $normalised = strtolower(trim((string) $value));
                $data[$field] = $priorityMap[$normalised] ?? null;
            } elseif ($field === 'franchise') {
                $data['franchise_id'] = $this->matchFranchise($value);
            } elseif ($field === 'template') {
                $data['template_id'] = $this->matchTemplate($value);
            } elseif ($field === 'pageviews' || $field === 'key_events') {
                $data[$field] = $this->parseInteger($value);
            } elseif ($field === 'avg_engagement') {
                $data[$field] = $this->parseEngagement($value);
            } else {
                $data[$field] = $value;
            }
        }

        if (!empty($unmapped)) {
            $data['data'] = $unmapped;
        }

        return $data;
    }

    private function parseBoolean($value): bool
    {
        if (is_bool($value)) {
            return $value;
        }

        return in_array(strtolower(trim((string) $value)), ['yes', 'true', '1', 'done', 'y']);
    }

    private function parseInteger($value): ?int
    {
        if ($value === null || $value === '') {
            return null;
        }

        $stripped = preg_replace('/[^0-9-]/', '', (string) $value);
        if ($stripped === '' || $stripped === '-') {
            return null;
        }

        return (int) $stripped;
    }

    private function parseEngagement($value): ?float
    {
        if ($value === null || $value === '') {
            return null;
        }

        $raw = strtolower(trim((string) $value));

        // GA4 sometimes formats engagement time as "1m 23s" or "1h 4m 12s".
        if (preg_match('/[hms]/', $raw)) {
            $hours = 0;
            $mins = 0;
            $secs = 0.0;
            if (preg_match('/(\d+(?:\.\d+)?)\s*h/', $raw, $m)) {
                $hours = (float) $m[1];
            }
            if (preg_match('/(\d+(?:\.\d+)?)\s*m(?!s)/', $raw, $m)) {
                $mins = (float) $m[1];
            }
            if (preg_match('/(\d+(?:\.\d+)?)\s*s/', $raw, $m)) {
                $secs = (float) $m[1];
            }
            return ($hours * 3600) + ($mins * 60) + $secs;
        }

        $stripped = preg_replace('/[^0-9.\-]/', '', $raw);
        if ($stripped === '' || $stripped === '-' || $stripped === '.') {
            return null;
        }

        return (float) $stripped;
    }

    private function matchFranchise($value): ?int
    {
        if (empty($value)) {
            return null;
        }

        return $this->franchises->first(
            fn ($id, $name) => strcasecmp($name, trim($value)) === 0
        );
    }

    private function matchTemplate($value): ?int
    {
        if (empty($value)) {
            return null;
        }

        return $this->templates->first(
            fn ($id, $name) => strcasecmp($name, trim($value)) === 0
        );
    }

    public function getResults(): array
    {
        return [
            'imported' => $this->imported,
            'merged' => $this->merged,
            'skipped' => $this->skipped,
            'errors' => $this->errors,
        ];
    }
}
