import re
from datetime import datetime
from typing import Any, Literal

from pydantic import BaseModel, ConfigDict, EmailStr, Field, field_validator

from app.core import sensitive
from app.schemas.user import CN_PHONE_PATTERN
from app.schemas.membership import UserMembershipResponse

CN_ID_CARD_PATTERN = re.compile(r"^\d{17}[\dX]$")
CN_ID_CARD_WEIGHTS = (7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2)
CN_ID_CARD_CHECK_CODES = "10X98765432"


def normalize_cn_id_card(value: str | None) -> str | None:
    if value is None:
        return None
    normalized = "".join(str(value).split()).upper()
    if not normalized:
        return None
    if not CN_ID_CARD_PATTERN.fullmatch(normalized):
        raise ValueError("身份证号格式不正确")
    try:
        datetime.strptime(normalized[6:14], "%Y%m%d")
    except ValueError as exc:
        raise ValueError("身份证号出生日期不正确") from exc
    checksum = sum(int(digit) * weight for digit, weight in zip(normalized[:17], CN_ID_CARD_WEIGHTS))
    if CN_ID_CARD_CHECK_CODES[checksum % 11] != normalized[-1]:
        raise ValueError("身份证号校验位不正确")
    return normalized


class AdminUserListItem(BaseModel):
    """用户列表项（精简）。"""

    model_config = ConfigDict(from_attributes=True)
    id: int
    email: str | None
    username: str
    phone: str | None
    nickname: str | None
    real_name: str | None
    avatar: str | None
    role: str
    status: str
    id_card_verified: str | None
    is_email_verified: bool
    is_phone_verified: bool
    has_password: bool
    balance: int
    full_refund_quota_extra: int = 0
    full_refund_quota_reset_at: datetime | None = None
    last_login_at: datetime | None
    created_at: datetime

class AdminUserRefundQuota(BaseModel):
    base_limit: int
    extra: int
    limit: int
    used: int
    remaining: int | None
    unlimited: bool
    reset_at: datetime | None


class AdminUserInviteCodeQuota(BaseModel):
    current_code: str | None
    base_limit: int
    extra: int
    limit: int
    used: int
    remaining: int


class AdminUserInviteCodeItem(BaseModel):
    id: int
    code: str
    is_current: bool
    source: str
    created_at: datetime

    model_config = ConfigDict(from_attributes=True)


class AdminUserInviteCodeLogItem(BaseModel):
    id: int
    action: str
    count: int
    before_used: int
    after_used: int
    before_extra: int
    after_extra: int
    reason: str
    operator_id: int | None
    created_at: datetime

    model_config = ConfigDict(from_attributes=True)


class AdminUserVerificationQuota(BaseModel):
    free_quota_per_user: int
    purchase_enabled: bool
    price_cents: int
    free_used: int
    free_remaining: int
    paid_remaining: int
    paid_purchased: int
    paid_used: int
    remaining_total: int
    balance_cents: int
    can_submit: bool
    can_purchase: bool


class AdminUserVerificationRecordItem(BaseModel):
    id: int
    provider: str
    mode: str
    status: str
    provider_task_id: str | None
    real_name: str | None
    id_card_no: str | None
    id_card_front_image: str | None = None
    id_card_back_image: str | None = None
    id_card_face_image: str | None = None
    reject_reason: str | None
    provider_response: dict[str, Any] | None = None
    material_expires_at: datetime | None = None
    material_cleaned: bool = False
    verified_at: datetime | None
    rejected_at: datetime | None
    created_at: datetime


class AdminUserTicketItem(BaseModel):
    id: int
    ticket_no: str
    subject: str
    status: str
    priority: str
    department_name: str | None
    instance_id: int | None
    assignee_id: int | None
    admin_unread_count: int
    user_unread_count: int
    satisfaction_rating: int | None
    last_message_at: datetime | None
    closed_at: datetime | None
    created_at: datetime


class AdminUserDetailStats(BaseModel):
    instances_total: int = 0
    instances_active: int = 0
    instances_suspended: int = 0
    orders_total: int = 0
    orders_paid_amount: int = 0
    balance_logs_total: int = 0
    verification_records_total: int = 0
    tickets_total: int = 0
    tickets_open: int = 0
    tickets_unread: int = 0


class AdminUserDetail(AdminUserListItem):
    """用户详情，目前与列表项字段一致；将来如需嵌入订单/服务可在此扩展。"""

    id_card_no: str | None
    real_name: str | None
    id_card_no_full: str | None = None
    real_name_full: str | None = None
    id_card_front_image: str | None = None
    id_card_back_image: str | None = None
    id_card_face_image: str | None = None
    id_card_verify_remark: str | None = None
    membership: UserMembershipResponse | None = None
    refund_quota: AdminUserRefundQuota | None = None
    invite_code_quota: AdminUserInviteCodeQuota | None = None
    invite_codes: list[AdminUserInviteCodeItem] = []
    invite_code_logs: list[AdminUserInviteCodeLogItem] = []
    verification_quota: AdminUserVerificationQuota | None = None
    verification_records: list[AdminUserVerificationRecordItem] = []
    tickets: list[AdminUserTicketItem] = []
    stats: AdminUserDetailStats = Field(default_factory=AdminUserDetailStats)

    @field_validator("real_name", "real_name_full", mode="before")
    @classmethod
    def reveal_real_name(cls, value: str | None) -> str | None:
        return sensitive.decrypt_optional(value) or value

    @field_validator("id_card_no", "id_card_no_full", mode="before")
    @classmethod
    def reveal_id_card_no(cls, value: str | None) -> str | None:
        return sensitive.decrypt_optional(value) or value


class AdminUserUpdate(BaseModel):
    nickname: str | None = Field(None, max_length=100)
    avatar: str | None = None
    email: EmailStr | None = None
    phone: str | None = Field(None, pattern=CN_PHONE_PATTERN)
    real_name: str | None = Field(None, max_length=50)
    id_card_no: str | None = Field(None, max_length=32)
    id_card_verified: Literal["unverified", "pending", "verified", "rejected"] | None = None
    member_level_id: int | None = Field(None, ge=1)
    member_level_locked: bool | None = None
    agent_tier: Literal["yunqi", "yunxiang", "yunce", "yunling", "yunzun", "yunguan"] | None = None
    role: Literal["user", "admin", "super_admin"] | None = None
    status: Literal["active", "inactive", "banned"] | None = None
    is_email_verified: bool | None = None
    is_phone_verified: bool | None = None

    @field_validator("agent_tier", mode="before")
    @classmethod
    def normalize_agent_tier(cls, value: str | None) -> str | None:
        if value is None:
            return None
        text = str(value).strip()
        return text if text else None

    @field_validator("id_card_no", mode="before")
    @classmethod
    def validate_id_card_no(cls, value: str | None) -> str | None:
        if sensitive.is_masked(value):
            return str(value).strip().upper()
        return normalize_cn_id_card(value)


class AdminUserPasswordReset(BaseModel):
    new_password: str = Field(..., min_length=8, max_length=128)


class AdminBalanceAdjust(BaseModel):
    """调整用户余额。delta 单位：分。正数为充值，负数为扣款。"""

    delta: int = Field(..., description="变动金额，单位：分（cents）")
    reason: str | None = Field(None, max_length=200)


class AdminRefundQuotaAdjust(BaseModel):
    count: int = Field(..., ge=1, le=100)
    reason: str = Field(..., min_length=1, max_length=200)


class AdminRefundQuotaReset(BaseModel):
    reason: str = Field(..., min_length=1, max_length=200)


class AdminInviteCodeQuotaAdjust(BaseModel):
    count: int = Field(..., ge=1, le=100)
    reason: str = Field(..., min_length=1, max_length=200)


class AdminInviteCodeQuotaReset(BaseModel):
    reason: str = Field(..., min_length=1, max_length=200)


class AdminVerificationQuotaAdjust(BaseModel):
    count: int = Field(..., ge=1, le=1000)
    reason: str = Field(..., min_length=1, max_length=200)


class AdminVerificationQuotaReset(BaseModel):
    reason: str = Field(..., min_length=1, max_length=200)


class ImpersonateResponse(BaseModel):
    """一键登录响应：返回目标用户的 access/refresh token。"""

    access_token: str
    refresh_token: str
    token_type: str = "bearer"
    user_id: int
    username: str
