import csv
import json
import re
from pathlib import Path

from django.contrib.auth.models import User
from django.core.management.base import BaseCommand

from core.models import CustomerTemplateMapping
from core.utils import SHEET_NAMES


SKIPPED_SHEET_KEYS = {"hardness_out_in_4"}
INDEXED_SHEET_KEYS = [
    key for key in SHEET_NAMES.keys() if key not in SKIPPED_SHEET_KEYS
]


class Command(BaseCommand):
    help = "Import CustomerTemplateMapping records from legacy JSON CSV or tabular template-index data"

    def add_arguments(self, parser):
        parser.add_argument("csv_path", type=str, help="Path to the source file")
        parser.add_argument("--created_by", type=int, help="User ID to assign as created_by")
        parser.add_argument(
            "--export-template",
            dest="export_template",
            choices=[choice[0] for choice in CustomerTemplateMapping.TEMPLATE_TYPE_CHOICES],
            default="asean",
            help="Export template value to assign to imported rows",
        )

    def handle(self, *args, **options):
        source_path = Path(options["csv_path"])
        if not source_path.exists():
            self.stderr.write(self.style.ERROR(f"Source file not found: {source_path}"))
            return

        created_by = self.get_created_by(options.get("created_by"))
        if options.get("created_by") and created_by is None:
            return

        rows = self.read_rows(source_path)
        if not rows:
            self.stderr.write(self.style.WARNING(f"No rows found in {source_path}"))
            return

        if self.is_legacy_json_format(rows):
            count = self.import_legacy_json_rows(
                rows,
                created_by=created_by,
                export_template=options["export_template"],
            )
        else:
            count = self.import_tabular_rows(
                rows,
                created_by=created_by,
                export_template=options["export_template"],
            )

        self.stdout.write(self.style.SUCCESS(f"Imported {count} customer template mappings."))

    def get_created_by(self, user_id):
        if not user_id:
            return None
        try:
            return User.objects.get(id=user_id)
        except User.DoesNotExist:
            self.stdout.write(self.style.ERROR(f"User with ID {user_id} does not exist."))
            return None

    def read_rows(self, source_path):
        with source_path.open(newline="", encoding="utf-8-sig") as handle:
            sample = handle.read(2048)
            handle.seek(0)

            if "\t" in sample:
                dialect = csv.excel_tab
            else:
                try:
                    dialect = csv.Sniffer().sniff(sample, delimiters=",")
                except csv.Error:
                    dialect = csv.excel

            reader = csv.reader(handle, dialect)
            return [row for row in reader if any(cell.strip() for cell in row)]

    def is_legacy_json_format(self, rows):
        header = [cell.strip() for cell in rows[0]]
        return "customer_name" in header and "template_names" in header

    def import_legacy_json_rows(self, rows, created_by, export_template):
        header = [cell.strip() for cell in rows[0]]
        count = 0

        for row in rows[1:]:
            data = dict(zip(header, row))
            customer_name = data.get("customer_name", "").strip()
            template_names = data.get("template_names", "").strip()

            if not customer_name or not template_names:
                self.stderr.write(f"Skipping incomplete row: {data}")
                continue

            try:
                template_list = json.loads(template_names)
            except json.JSONDecodeError:
                self.stderr.write(f"Skipping row with invalid JSON: {template_names}")
                continue

            CustomerTemplateMapping.objects.update_or_create(
                customer_name=customer_name,
                defaults={
                    "template_names": template_list,
                    "export_template": export_template,
                    "created_by": created_by,
                },
            )
            count += 1

        return count

    def import_tabular_rows(self, rows, created_by, export_template):
        merged = {}

        for row in rows:
            if len(row) < 6:
                self.stderr.write(f"Skipping incomplete row: {row}")
                continue

            customer_name = self.normalize_customer_name(row[1])
            template_index = row[3].strip()

            if not customer_name:
                self.stderr.write(f"Skipping row with empty customer_name: {row}")
                continue

            try:
                template_key = self.template_key_from_index(template_index)
            except ValueError as exc:
                self.stderr.write(f"Skipping row {row}: {exc}")
                continue

            merged.setdefault(customer_name, set()).add(template_key)

        count = 0
        for customer_name, template_names in merged.items():
            ordered_templates = [
                key for key in INDEXED_SHEET_KEYS if key in template_names
            ]
            CustomerTemplateMapping.objects.update_or_create(
                customer_name=customer_name,
                defaults={
                    "template_names": ordered_templates,
                    "export_template": export_template,
                    "created_by": created_by,
                },
            )
            count += 1

        return count

    def normalize_customer_name(self, value):
        cleaned = re.sub(r"\s+", " ", value or "")
        return cleaned.strip()

    def template_key_from_index(self, raw_value):
        try:
            index = int(raw_value)
        except (TypeError, ValueError) as exc:
            raise ValueError(f"invalid template index '{raw_value}'") from exc

        if index < 1 or index > len(INDEXED_SHEET_KEYS):
            raise ValueError(
                f"template index '{raw_value}' is out of range 1..{len(INDEXED_SHEET_KEYS)}"
            )

        return INDEXED_SHEET_KEYS[index - 1]
