import json
import logging

from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from sqlalchemy.orm import selectinload
from typing import List, Optional, Any
from datetime import datetime

from app.api.deps import get_db, get_tenant_by_domain
from app.api.routers.store.auth import get_current_customer
from app.core.models.customer import Customer
from app.core.models.customer_address import CustomerAddress
from app.core.models.tenant_settings import TenantSettings
from app.core.models.shipping_zone import ShippingZone
from app.core.models.country import Country
from app.core.ai_utils import call_ai, resolve_ai_extra
from app.core.services.ai_quota import consume_ai_quota
from pydantic import BaseModel, Field, field_validator

logger = logging.getLogger(__name__)

router = APIRouter(prefix="/store/addresses", tags=["收货地址"])


class AddressOut(BaseModel):
    id: int
    name: str
    phone: str
    country: str = "NZ"
    zip_code: str = ""
    province: str
    city: str
    district: str
    street: str
    is_default: bool
    extra_fields: dict = {}
    created_at: Optional[datetime] = None

    model_config = {"from_attributes": True}

    @field_validator('extra_fields', mode='before')
    @classmethod
    def coerce_extra_fields(cls, v: Any) -> dict:
        return v or {}


class AddressCreate(BaseModel):
    name: str = Field(..., max_length=50)
    phone: str = Field(..., max_length=20)
    country: str = Field("NZ", max_length=2)
    zip_code: str = Field("", max_length=20)
    province: str = Field(..., max_length=100)
    city: str = Field(..., max_length=100)
    district: str = Field("", max_length=100)
    street: str = Field(..., max_length=200)
    is_default: bool = False
    extra_fields: dict = {}


class AddressUpdate(BaseModel):
    name: Optional[str] = None
    phone: Optional[str] = None
    country: Optional[str] = None
    zip_code: Optional[str] = None
    province: Optional[str] = None
    city: Optional[str] = None
    district: Optional[str] = None
    street: Optional[str] = None
    is_default: Optional[bool] = None
    extra_fields: Optional[dict] = None


@router.get("", response_model=List[AddressOut])
async def list_addresses(db: AsyncSession = Depends(get_db), customer: Customer = Depends(get_current_customer)):
    result = await db.execute(
        select(CustomerAddress)
        .where(CustomerAddress.customer_id == customer.id)
        .order_by(CustomerAddress.is_default.desc(), CustomerAddress.id.desc())
    )
    return result.scalars().all()


@router.post("", response_model=AddressOut, status_code=status.HTTP_201_CREATED)
async def create_address(body: AddressCreate, db: AsyncSession = Depends(get_db), customer: Customer = Depends(get_current_customer)):
    if body.is_default:
        await db.execute(
            CustomerAddress.__table__.update()
            .where(CustomerAddress.customer_id == customer.id)
            .values(is_default=0)
        )

    addr = CustomerAddress(
        tenant_id=customer.tenant_id,
        customer_id=customer.id,
        name=body.name,
        phone=body.phone,
        country=body.country,
        zip_code=body.zip_code,
        province=body.province,
        city=body.city,
        district=body.district or "",
        street=body.street,
        is_default=1 if body.is_default else 0,
        extra_fields=body.extra_fields or {},
    )
    db.add(addr)
    await db.commit()
    await db.refresh(addr)
    return addr


@router.put("/{address_id}", response_model=AddressOut)
async def update_address(address_id: int, body: AddressUpdate, db: AsyncSession = Depends(get_db), customer: Customer = Depends(get_current_customer)):
    result = await db.execute(
        select(CustomerAddress).where(
            CustomerAddress.id == address_id,
            CustomerAddress.customer_id == customer.id,
        )
    )
    addr = result.scalar_one_or_none()
    if not addr:
        raise HTTPException(status_code=404, detail="地址不存在")

    if body.is_default:
        await db.execute(
            CustomerAddress.__table__.update()
            .where(CustomerAddress.customer_id == customer.id)
            .values(is_default=0)
        )

    update_data = body.model_dump(exclude_unset=True)
    for k, v in update_data.items():
        if k == "is_default":
            setattr(addr, k, 1 if v else 0)
        else:
            setattr(addr, k, v)

    await db.commit()
    await db.refresh(addr)
    return addr


@router.delete("/{address_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_address(address_id: int, db: AsyncSession = Depends(get_db), customer: Customer = Depends(get_current_customer)):
    result = await db.execute(
        select(CustomerAddress).where(
            CustomerAddress.id == address_id,
            CustomerAddress.customer_id == customer.id,
        )
    )
    addr = result.scalar_one_or_none()
    if not addr:
        raise HTTPException(status_code=404, detail="地址不存在")

    await db.delete(addr)
    await db.commit()


class ParseAddressIn(BaseModel):
    text: str = Field(..., min_length=5, max_length=500)


class ParseAddressOut(BaseModel):
    name: str = ""
    phone: str = ""
    country: str = ""
    country_name: str = ""
    province: str = ""
    city: str = ""
    district: str = ""
    street: str = ""
    zip_code: str = ""
    extra_fields: dict[str, str] = {}
    warnings: list[str] = []


@router.post("/parse", response_model=ParseAddressOut, summary="AI 智能解析地址文本")
async def parse_address(
    body: ParseAddressIn,
    customer: Customer = Depends(get_current_customer),
    tid: int = Depends(get_tenant_by_domain),
    db: AsyncSession = Depends(get_db),
):
    # 1. 加载租户 AI 配置
    ts_r = await db.execute(select(TenantSettings).where(TenantSettings.tenant_id == tid))
    ts = ts_r.scalar_one_or_none()
    extra = (ts.extra if ts and ts.extra else {}) if ts else {}
    if not extra.get("ai_enabled"):
        raise HTTPException(400, "AI 功能未启用，请在系统设置中开启")

    # 2. 加载该租户支持的国家和省份（从 shipping_zones 提取）
    zone_r = await db.execute(
        select(ShippingZone.countries, ShippingZone.province_rules)
        .where(ShippingZone.tenant_id == tid, ShippingZone.is_active == 1)
    )
    supported_codes: set[str] = set()
    supported_provinces: dict[str, list[str]] = {}
    for countries_list, province_rules in zone_r.all():
        if countries_list:
            for c in countries_list:
                supported_codes.add(c.strip().upper())
        if province_rules:
            for code, provs in province_rules.items():
                supported_provinces.setdefault(code.upper(), []).extend(provs)

    # 加载国家名称映射
    country_rows = (await db.execute(
        select(Country.code, Country.name_zh, Country.name_en)
        .where(Country.code.in_(supported_codes), Country.is_active == 1)
    )).all() if supported_codes else []
    country_names = {r.code: f"{r.name_zh}({r.name_en})" for r in country_rows}

    # 如果 zones 没配国家，也查全部 country 表作为候选
    if not supported_codes:
        all_countries = (await db.execute(
            select(Country.code, Country.name_zh, Country.name_en).where(Country.is_active == 1)
        )).all()
        country_names = {r.code: f"{r.name_zh}({r.name_en})" for r in all_countries}
        supported_codes = set(country_names.keys())

    countries_hint = ", ".join(f"{code}={country_names.get(code, code)}" for code in sorted(supported_codes))
    provinces_hint = ""
    for code, provs in supported_provinces.items():
        provinces_hint += f"\n{code}的省份: {', '.join(provs)}"

    # 3. 加载自定义地址字段
    custom_fields: list[dict] = extra.get("address_custom_fields") or []
    custom_fields_hint = ""
    custom_fields_json = ""
    if custom_fields:
        labels = [f'"{f["key"]}"({f.get("label", f["key"])})' for f in custom_fields]
        custom_fields_hint = f"\n该商店还有以下自定义地址字段，如果文本中有相关信息请提取：{', '.join(labels)}"
        custom_fields_json = ',"extra_fields":{' + ','.join(f'"{f["key"]}":"值"' for f in custom_fields) + '}'

    # 4. 构造 prompt
    system_prompt = f"""You are a professional address parser. Parse the user's address text into structured JSON.
Support both Chinese and English addresses. Output ONLY valid JSON, nothing else.

Output format:
{{"name":"recipient name","phone":"phone number","country":"2-letter country code (uppercase)","province":"state/province full name","city":"city name","district":"district/suburb","street":"street address (exclude province/city/district)","zip_code":"postal code"{custom_fields_json}}}

Supported shipping countries: {countries_hint}
{provinces_hint}{custom_fields_hint}

=== CHINESE ADDRESS RULES ===
- Split into province → city → district → street. Fill ALL four levels.
- Example: "广州番禺石基大龙街道汉基大道6号" → province="广东省", city="广州市", district="番禺区", street="石基大龙街道汉基大道6号"
- Example: "深圳南山区科技园南路" → province="广东省", city="深圳市", district="南山区", street="科技园南路"
- Infer province from city if not given (广州→广东省, 成都→四川省, 昆明→云南省, etc.)
- city MUST have "市" suffix (广州市, not 广州). district MUST have "区/县/旗" suffix (番禺区, not 番禺)
- If text contains Chinese province/city/district names, country is always CN

=== ENGLISH ADDRESS RULES ===
- Example: "John Smith, 123 Queen St, Parnell, Auckland 1052, New Zealand, +64 21 123 4567"
  → name="John Smith", phone="211234567", street="123 Queen St, Parnell", city="Auckland", province="Auckland", district="", zip_code="1052", country="NZ"
- Example: "Jane Doe 0412345678, Unit 5/42 George St, Sydney NSW 2000"
  → name="Jane Doe", phone="0412345678", street="Unit 5/42 George St", city="Sydney", province="New South Wales", district="", zip_code="2000", country="AU"
- Example: "Mike Johnson, 350 Fifth Avenue, New York, NY 10118"
  → name="Mike Johnson", phone="", street="350 Fifth Avenue", city="New York", province="New York", district="", zip_code="10118", country="US"
- For NZ: province = region name (Auckland, Wellington, Canterbury, Otago, Waikato, Bay of Plenty, etc.)
- For AU: province = full state name (New South Wales, Victoria, Queensland, etc.), NOT abbreviations (NSW, VIC)
- For US/CA/UK: province = state/province full name
- street = street number + street name + unit/apartment. Do NOT include city/state/country in street.
- Recognize common formats: "Unit X/Y Street", "Flat X, Y Street", "X/Y Street", "PO Box X"

=== GENERAL RULES ===
Name:
- Keep the FULL name from the text, including titles like 先生/小姐/女士/Mr/Mrs/Ms/Miss/Dr
- "罗小姐" must stay as "罗小姐", NOT "罗"
- Name can appear at the start OR end of the text

Phone:
- Extract digits only, remove country code prefixes (+86, +64, +61, +1, etc.)
- Keep leading 0 for local numbers (e.g., AU: 0412345678, NZ: 021234567)

Postal code:
- If present in text, extract it
- If NOT in text, look up the correct postal code based on the parsed address (e.g., 广州番禺区=511400, Auckland CBD=1010, Sydney CBD=2000)
- Leave empty only if you truly cannot determine it

Other:
- Leave unrecognizable fields as empty string"""

    # 4. 调用 AI
    extra = await resolve_ai_extra(db, tid, extra)
    await consume_ai_quota(db, tid)
    try:
        raw = await call_ai(body.text, extra, system_prompt=system_prompt, max_tokens=512, timeout=30)
    except HTTPException:
        raise
    except Exception as e:
        logger.warning("AI address parse failed: %s", e)
        raise HTTPException(502, f"AI 解析失败：{str(e)[:100]}")

    # 5. 解析 JSON
    try:
        raw_clean = raw.strip()
        if raw_clean.startswith("```"):
            raw_clean = raw_clean.split("\n", 1)[1] if "\n" in raw_clean else raw_clean[3:]
            raw_clean = raw_clean.rsplit("```", 1)[0].strip()
        parsed = json.loads(raw_clean)
    except (json.JSONDecodeError, IndexError):
        logger.warning("AI returned non-JSON: %s", raw[:200])
        raise HTTPException(502, "AI 返回格式异常，请重试")

    # 6. 校验并修正
    warnings: list[str] = []
    country = (parsed.get("country") or "").strip().upper()
    province = (parsed.get("province") or "").strip()

    # 校验国家
    if country and country not in supported_codes:
        warnings.append(f"该国家（{country}）暂不支持配送，请选择：{', '.join(sorted(supported_codes))}")
        country = ""
    country_display = country_names.get(country, country)

    # 校验省份
    if country and province and country in supported_provinces:
        valid_provinces = supported_provinces[country]
        if province not in valid_provinces:
            # 尝试模糊匹配（如"广东" → "广东省"）
            matched = None
            for vp in valid_provinces:
                if province in vp or vp in province:
                    matched = vp
                    break
            if matched:
                province = matched
            else:
                warnings.append(f"省份「{province}」不在该国家的配送范围内")

    # 提取自定义字段
    ai_extra = parsed.get("extra_fields") or {}
    extra_fields_out = {}
    for f in custom_fields:
        val = (ai_extra.get(f["key"]) or "").strip()
        if val:
            extra_fields_out[f["key"]] = val

    return ParseAddressOut(
        name=(parsed.get("name") or "").strip(),
        phone=(parsed.get("phone") or "").strip(),
        country=country,
        country_name=country_display,
        province=province,
        city=(parsed.get("city") or "").strip(),
        district=(parsed.get("district") or "").strip(),
        street=(parsed.get("street") or "").strip(),
        zip_code=(parsed.get("zip_code") or "").strip(),
        extra_fields=extra_fields_out,
        warnings=warnings,
    )


@router.post("/{address_id}/default", response_model=AddressOut)
async def set_default_address(address_id: int, db: AsyncSession = Depends(get_db), customer: Customer = Depends(get_current_customer)):
    result = await db.execute(
        select(CustomerAddress).where(
            CustomerAddress.id == address_id,
            CustomerAddress.customer_id == customer.id,
        )
    )
    addr = result.scalar_one_or_none()
    if not addr:
        raise HTTPException(status_code=404, detail="地址不存在")

    await db.execute(
        CustomerAddress.__table__.update()
        .where(CustomerAddress.customer_id == customer.id)
        .values(is_default=0)
    )
    addr.is_default = 1
    await db.commit()
    await db.refresh(addr)
    return addr