"""Bulk assign collection-level default store-location settings.

Examples:
    python -m app.jobs.set_collection_location_defaults --channel magento --connection-id 4 --default-location-code 2 --apply
    python -m app.jobs.set_collection_location_defaults --channel shopify --connection-id 7 --default-location-code 1 --collection "Anna"
"""

from __future__ import annotations

import argparse
import json
from typing import Any, Dict, List, Optional

from sqlalchemy import select

from db.catalog_normalization_service import upsert_collection_commerce_profile
from db.location_registry import active_location_directory
from db.models import CollectionCommerceProfile, MasterCollectionRegistry
from db.session import get_session


def run_set_collection_location_defaults(
    *,
    channel_code: str,
    connection_id: Optional[int],
    default_location_code: str,
    category_l1: Optional[str] = "Kitchen Cabinets",
    collection_name: Optional[str] = None,
    allow_geolocation: bool = True,
    allow_manual_selection: bool = True,
    location_selection_mode: str = "manual_with_optional_geolocation",
    location_label: str = "Store Location",
    create_missing_profiles: bool = True,
    session=None,
    apply: bool = False,
) -> Dict[str, Any]:
    owns_session = session is None
    if session is None:
        session_cm = get_session()
        session = session_cm.__enter__()
    try:
        directory = active_location_directory(session)
        valid_codes = {str(item.get("code")) for item in directory if str(item.get("code") or "").strip()}
        if str(default_location_code).strip() not in valid_codes:
            raise ValueError(f"default_location_code {default_location_code!r} is not in active master locations: {sorted(valid_codes)}")

        existing_rows = list(
            session.scalars(
                _profile_stmt(
                    channel_code=channel_code,
                    connection_id=connection_id,
                    category_l1=category_l1,
                    collection_name=collection_name,
                )
            ).all()
        )
        existing_by_registry_id = {
            int(row.collection_registry_id): row
            for row in existing_rows
            if row.collection_registry_id is not None
        }

        targets: List[tuple[Optional[CollectionCommerceProfile], MasterCollectionRegistry]] = []
        registries = list(
            session.scalars(
                _registry_stmt(category_l1=category_l1, collection_name=collection_name)
            ).all()
        )
        for registry in registries:
            row = existing_by_registry_id.get(int(registry.id))
            if row is not None:
                targets.append((row, registry))
            elif create_missing_profiles:
                targets.append((None, registry))

        updated = 0
        created = 0
        samples: List[Dict[str, Any]] = []
        for row, registry in targets:
            payload = _payload_for_row_or_registry(
                row=row,
                registry=registry,
                channel_code=channel_code,
                connection_id=connection_id,
                default_location_code=default_location_code,
                allow_geolocation=allow_geolocation,
                allow_manual_selection=allow_manual_selection,
                location_selection_mode=location_selection_mode,
                location_label=location_label,
            )
            result = upsert_collection_commerce_profile(
                session,
                profile_id=int(row.id) if row is not None else None,
                payload=payload,
            )
            if row is None:
                created += 1
            else:
                updated += 1
            if len(samples) < 25:
                samples.append(
                    {
                        "collection_name": result.get("collection_name"),
                        "channel_code": result.get("channel_code"),
                        "connection_id": result.get("connection_id"),
                        "default_location_code": result.get("default_location_code"),
                    }
                )

        if apply:
            session.commit()
        else:
            session.rollback()

        return {
            "applied": bool(apply),
            "channel_code": channel_code,
            "connection_id": connection_id,
            "default_location_code": default_location_code,
            "category_l1": category_l1,
            "collection_name_filter": collection_name,
            "create_missing_profiles": create_missing_profiles,
            "matched_collection_count": len(registries),
            "updated_profile_count": updated,
            "created_profile_count": created,
            "sample": samples,
        }
    finally:
        if owns_session:
            session_cm.__exit__(None, None, None)


def _profile_stmt(
    *,
    channel_code: str,
    connection_id: Optional[int],
    category_l1: Optional[str],
    collection_name: Optional[str],
):
    stmt = (
        select(CollectionCommerceProfile)
        .where(CollectionCommerceProfile.channel_code == str(channel_code).strip().lower())
        .order_by(CollectionCommerceProfile.collection_name, CollectionCommerceProfile.id)
    )
    if connection_id is None:
        stmt = stmt.where(CollectionCommerceProfile.connection_id.is_(None))
    else:
        stmt = stmt.where(CollectionCommerceProfile.connection_id == int(connection_id))
    if category_l1:
        stmt = stmt.where(
            (CollectionCommerceProfile.category_l1.is_(None))
            | (CollectionCommerceProfile.category_l1.ilike(str(category_l1).strip()))
        )
    if collection_name:
        stmt = stmt.where(CollectionCommerceProfile.collection_name.ilike(f"%{str(collection_name).strip()}%"))
    return stmt


def _registry_stmt(
    *,
    category_l1: Optional[str],
    collection_name: Optional[str],
):
    stmt = (
        select(MasterCollectionRegistry)
        .where(MasterCollectionRegistry.is_active.is_(True))
        .order_by(MasterCollectionRegistry.name, MasterCollectionRegistry.id)
    )
    if collection_name:
        stmt = stmt.where(MasterCollectionRegistry.name.ilike(f"%{str(collection_name).strip()}%"))
    if category_l1:
        stmt = stmt.where(MasterCollectionRegistry.path_slug.ilike(f"{_slugify(category_l1)}/%"))
    return stmt


def _payload_for_row_or_registry(
    *,
    row: Optional[CollectionCommerceProfile],
    registry: MasterCollectionRegistry,
    channel_code: str,
    connection_id: Optional[int],
    default_location_code: str,
    allow_geolocation: bool,
    allow_manual_selection: bool,
    location_selection_mode: str,
    location_label: str,
) -> Dict[str, Any]:
    return {
        "collection_registry_id": registry.id,
        "category_l1": row.category_l1 if row is not None else _category_from_registry(registry),
        "collection_name": row.collection_name if row is not None else registry.name,
        "channel_code": row.channel_code if row is not None else channel_code,
        "connection_id": row.connection_id if row is not None else connection_id,
        "sample_master_sku": row.sample_master_sku if row is not None else None,
        "sample_category_path_slug": row.sample_category_path_slug if row is not None else None,
        "availability_modes_json": row.availability_modes_json if row is not None else None,
        "appointment_url": row.appointment_url if row is not None else None,
        "sample_cta_label": row.sample_cta_label if row is not None else None,
        "appointment_cta_label": row.appointment_cta_label if row is not None else None,
        "badges_json": row.badges_json if row is not None else None,
        "landing_metadata_json": row.landing_metadata_json if row is not None else None,
        "notes": row.notes if row is not None else None,
        "is_active": row.is_active if row is not None else True,
        "default_location_code": default_location_code,
        "allow_geolocation": allow_geolocation,
        "allow_manual_selection": allow_manual_selection,
        "location_selection_mode": location_selection_mode,
        "location_label": location_label,
    }


def _category_from_registry(registry: MasterCollectionRegistry) -> str:
    slug = str(registry.path_slug or "").strip().strip("/")
    head = slug.split("/", 1)[0] if slug else ""
    return head.replace("-", " ").title() if head else "Kitchen Cabinets"


def _slugify(value: str) -> str:
    return "-".join(part for part in str(value or "").strip().lower().split() if part)


def main() -> None:
    parser = argparse.ArgumentParser(description="Bulk set default store-location settings on collection commerce profiles.")
    parser.add_argument("--channel", required=True, choices=["magento", "shopify", "plytix"])
    parser.add_argument("--connection-id", type=int, default=None)
    parser.add_argument("--default-location-code", required=True)
    parser.add_argument("--category-l1", default="Kitchen Cabinets")
    parser.add_argument("--collection", default=None, help="Optional collection-name contains filter.")
    parser.add_argument("--disable-geolocation", action="store_true")
    parser.add_argument("--disable-manual-selection", action="store_true")
    parser.add_argument("--location-selection-mode", default="manual_with_optional_geolocation")
    parser.add_argument("--location-label", default="Store Location")
    parser.add_argument("--no-create-missing-profiles", action="store_true")
    parser.add_argument("--apply", action="store_true")
    args = parser.parse_args()

    result = run_set_collection_location_defaults(
        channel_code=args.channel,
        connection_id=args.connection_id,
        default_location_code=args.default_location_code,
        category_l1=args.category_l1,
        collection_name=args.collection,
        allow_geolocation=not args.disable_geolocation,
        allow_manual_selection=not args.disable_manual_selection,
        location_selection_mode=args.location_selection_mode,
        location_label=args.location_label,
        create_missing_profiles=not args.no_create_missing_profiles,
        apply=args.apply,
    )
    print(json.dumps(result, indent=2))


if __name__ == "__main__":
    main()
