"""前台顾客认证路由 — 注册 / 登录 / 个人信息"""
from datetime import datetime, timedelta
from typing import Optional

import jwt
import bcrypt
from fastapi import APIRouter, Depends, HTTPException
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from pydantic import BaseModel, EmailStr, Field
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, or_

from app.api.deps import get_db, get_tenant_by_domain
from app.config import settings
from app.core.models.customer import Customer
from app.core.models.tenant_settings import TenantSettings
from app.services.email import build_smtp_config, send_notification

router = APIRouter(prefix="/store/auth", tags=["前台认证"])
_bearer = HTTPBearer(auto_error=False)

class RegisterIn(BaseModel):
    name: str = Field(..., min_length=2, max_length=50)
    email: EmailStr
    password: str = Field(..., min_length=8)
    phone: Optional[str] = None
    tenant_id: Optional[int] = Field(None, description="租户 ID，不传则使用系统默认值")
    extra_fields: Optional[dict] = Field(None, description="租户配置的自定义字段值")


class LoginIn(BaseModel):
    account: str   # email 或手机号
    password: str
    tenant_id: Optional[int] = Field(None, description="租户 ID，不传则使用系统默认值")


class TokenOut(BaseModel):
    access_token: str
    token_type: str = "bearer"
    user: dict


class ProfileIn(BaseModel):
    name: Optional[str] = None
    phone: Optional[str] = None


class ChangePasswordIn(BaseModel):
    old_password: str = Field(..., min_length=1)
    new_password: str = Field(..., min_length=8)


def _hash_pwd(pwd: str) -> str:
    return bcrypt.hashpw(pwd.encode(), bcrypt.gensalt()).decode()


def _verify_pwd(pwd: str, hashed: str) -> bool:
    try:
        return bcrypt.checkpw(pwd.encode(), hashed.encode())
    except Exception:
        return False


def _issue_token(customer_id: int) -> str:
    payload = {
        "sub": str(customer_id),
        "role": "customer",
        "exp": datetime.utcnow() + timedelta(days=30),
    }
    return jwt.encode(payload, settings.SECRET_KEY, algorithm="HS256")


def _customer_to_dict(c: Customer) -> dict:
    return {
        "id": c.id,
        "name": c.name,
        "email": c.email,
        "phone": c.phone,
        "member_level_id": c.member_level_id,
        "points_balance": c.points_balance or 0,
        "total_spent": float(c.total_spent or 0),
        "orders_count": c.orders_count or 0,
        "is_vip": bool((c.tags or {}).get("vip")),
    }


async def get_current_customer(
    creds: Optional[HTTPAuthorizationCredentials] = Depends(_bearer),
    db: AsyncSession = Depends(get_db),
) -> Customer:
    if not creds:
        raise HTTPException(status_code=401, detail="请先登录")
    try:
        payload = jwt.decode(creds.credentials, settings.SECRET_KEY, algorithms=["HS256"])
        if payload.get("role") != "customer":
            raise HTTPException(status_code=403, detail="权限不足")
        cid = int(payload["sub"])
    except jwt.ExpiredSignatureError:
        raise HTTPException(status_code=401, detail="登录已过期，请重新登录")
    except Exception:
        raise HTTPException(status_code=401, detail="无效的认证凭证")

    r = await db.execute(select(Customer).where(Customer.id == cid))
    c = r.scalar_one_or_none()
    if not c:
        raise HTTPException(status_code=401, detail="顾客账号不存在")
    return c


@router.post("/register", response_model=TokenOut, summary="顾客注册")
async def register(
    body: RegisterIn,
    db: AsyncSession = Depends(get_db),
    tid: int = Depends(get_tenant_by_domain),
):
    r = await db.execute(
        select(Customer).where(Customer.email == body.email, Customer.tenant_id == tid)
    )
    if r.scalar_one_or_none():
        raise HTTPException(status_code=400, detail="该邮箱已注册")

    # 处理自定义字段
    from app.core.services.customer_fields import normalize_fields, filter_input, validate_required
    ts_r = await db.execute(select(TenantSettings).where(TenantSettings.tenant_id == tid))
    ts = ts_r.scalar_one_or_none()
    extra = (ts.extra or {}) if ts else {}
    enabled_fields = normalize_fields(extra.get("customer_profile_fields") or [])
    extra_data: dict = {"password_hash": _hash_pwd(body.password)}
    if enabled_fields:
        clean = filter_input(body.extra_fields or {}, enabled_fields)
        missing = validate_required(clean, enabled_fields)
        if missing:
            raise HTTPException(status_code=422, detail=f"以下必填字段缺失: {', '.join(missing)}")
        extra_data.update(clean)

    customer = Customer(
        tenant_id=tid,
        name=body.name,
        email=body.email,
        phone=body.phone,
        extra_data=extra_data,
    )
    db.add(customer)
    await db.commit()
    await db.refresh(customer)

    try:
        from app.tasks.email_tasks import send_notification_task
        from app.core.services.customer_fields import build_customer_email_vars
        send_notification_task.delay(
            tenant_id=tid,
            template_key="welcome",
            to_email=customer.email,
            variables={
                "name":       customer.name,
                "email":      customer.email,
                "store_name": ts.store_name if ts else "SME Store",
                **build_customer_email_vars(customer.extra_data or {}, enabled_fields),
            },
            smtp_config=build_smtp_config(ts),
        )
    except Exception:
        pass

    return TokenOut(access_token=_issue_token(customer.id), user=_customer_to_dict(customer))


@router.post("/login", response_model=TokenOut, summary="顾客登录")
async def login(
    body: LoginIn,
    db: AsyncSession = Depends(get_db),
    tid: int = Depends(get_tenant_by_domain),
):
    r = await db.execute(
        select(Customer).where(
            or_(Customer.email == body.account, Customer.phone == body.account),
            Customer.tenant_id == tid,
        )
    )
    customer = r.scalar_one_or_none()
    if not customer:
        raise HTTPException(status_code=401, detail="账号或密码错误")
    pwd_hash = (customer.extra_data or {}).get("password_hash", "")
    if not pwd_hash or not _verify_pwd(body.password, pwd_hash):
        raise HTTPException(status_code=401, detail="账号或密码错误")
    return TokenOut(access_token=_issue_token(customer.id), user=_customer_to_dict(customer))


@router.get("/me", summary="获取当前顾客信息")
async def me(customer: Customer = Depends(get_current_customer)):
    return _customer_to_dict(customer)


@router.patch("/me", summary="更新个人信息")
async def update_me(body: ProfileIn, customer: Customer = Depends(get_current_customer), db: AsyncSession = Depends(get_db)):
    if body.name:
        customer.name = body.name
    if body.phone:
        customer.phone = body.phone
    db.add(customer)
    await db.commit()
    await db.refresh(customer)
    return _customer_to_dict(customer)


@router.post("/change-password", summary="修改当前顾客密码")
async def change_password(
    body: ChangePasswordIn,
    customer: Customer = Depends(get_current_customer),
    db: AsyncSession = Depends(get_db),
):
    pwd_hash = (customer.extra_data or {}).get("password_hash", "")
    if not pwd_hash or not _verify_pwd(body.old_password, pwd_hash):
        raise HTTPException(status_code=400, detail="当前密码错误")
    customer.set_attribute("password_hash", _hash_pwd(body.new_password))
    db.add(customer)
    await db.commit()

    try:
        from datetime import datetime
        ts_r = await db.execute(select(TenantSettings).where(TenantSettings.tenant_id == customer.tenant_id))
        ts = ts_r.scalar_one_or_none()
        from app.tasks.email_tasks import send_notification_task
        send_notification_task.delay(
            tenant_id=customer.tenant_id,
            template_key="password_changed",
            to_email=customer.email,
            variables={
                "name":       customer.name,
                "time":       datetime.now().strftime("%Y-%m-%d %H:%M"),
                "store_name": ts.store_name if ts else "SME Store",
            },
            smtp_config=build_smtp_config(ts),
        )
    except Exception:
        pass

    return {"message": "密码修改成功"}


class ProfileFieldsIn(BaseModel):
    extra_fields: Optional[dict] = Field(None, description="自定义字段值")


@router.get("/profile", summary="获取顾客自定义字段资料")
async def get_profile(
    customer: Customer = Depends(get_current_customer),
    db: AsyncSession = Depends(get_db),
):
    from app.core.services.customer_fields import normalize_fields, filter_profile_data
    ts_r = await db.execute(select(TenantSettings).where(TenantSettings.tenant_id == customer.tenant_id))
    ts = ts_r.scalar_one_or_none()
    extra = (ts.extra or {}) if ts else {}
    enabled_fields = normalize_fields(extra.get("customer_profile_fields") or [])
    return {
        "profile_data": filter_profile_data(customer.extra_data or {}, enabled_fields),
        "fields_config": enabled_fields,
    }


@router.put("/profile", summary="更新顾客自定义字段资料")
async def update_profile(
    body: ProfileFieldsIn,
    customer: Customer = Depends(get_current_customer),
    db: AsyncSession = Depends(get_db),
):
    from app.core.services.customer_fields import normalize_fields, filter_input, validate_required
    ts_r = await db.execute(select(TenantSettings).where(TenantSettings.tenant_id == customer.tenant_id))
    ts = ts_r.scalar_one_or_none()
    extra = (ts.extra or {}) if ts else {}
    enabled_fields = normalize_fields(extra.get("customer_profile_fields") or [])
    if enabled_fields:
        clean = filter_input(body.extra_fields or {}, enabled_fields)
        # 合并：已有值 + 提交值，再校验 required（防止只提交部分字段时漏必填）
        current = dict(customer.extra_data or {})
        from app.core.services.customer_fields import filter_profile_data
        existing_profile = filter_profile_data(current, enabled_fields)
        merged = {**existing_profile, **clean}
        missing = validate_required(merged, enabled_fields)
        if missing:
            raise HTTPException(status_code=422, detail=f"以下必填字段缺失: {', '.join(missing)}")
        current.update(clean)
        customer.extra_data = current
        db.add(customer)
        await db.commit()
        await db.refresh(customer)
    return {
        "profile_data": filter_input(customer.extra_data or {}, enabled_fields),
        "fields_config": enabled_fields,
    }
