from typing import Optional
import re
from decimal import Decimal

from pydantic import BaseModel, Field, field_validator

PHONE_RE = re.compile(r"^\+?[0-9 ()-]{7,20}$")


def clean_phone(v: str) -> str:
    v = v.strip()
    if not PHONE_RE.match(v):
        raise ValueError("Enter a valid phone number.")
    return v


class Register(BaseModel):
    name: str = Field(min_length=2, max_length=120)
    email: str = Field(min_length=5, max_length=200)
    phone: str = ""
    password: str = Field(min_length=8, max_length=128)

    @field_validator("email")
    @classmethod
    def _email(cls, v):
        v = v.strip().lower()
        if not re.match(r"^[^@\s]+@[^@\s]+\.[^@\s]+$", v):
            raise ValueError("Enter a valid email address.")
        return v


class FirstOwner(BaseModel):
    code: str
    name: str = Field(min_length=2, max_length=120)
    email: str
    password: str = Field(min_length=8, max_length=128)


class Login(BaseModel):
    email: str
    password: str


class ProfileUpdate(BaseModel):
    name: str = Field(min_length=2, max_length=120)
    phone: str = ""
    address: str = ""


class PasswordChange(BaseModel):
    current: str
    new: str = Field(min_length=8, max_length=128)


class OrderLine(BaseModel):
    product_id: int
    quantity: int = Field(gt=0, le=50)


class Checkout(BaseModel):
    customer_name: str = Field(min_length=2, max_length=120)
    phone: str
    email: str = ""
    address: str = Field(min_length=5, max_length=500)
    zone_id: int
    payment_method: str
    note: str = Field(default="", max_length=500)
    items: list[OrderLine] = Field(min_length=1, max_length=60)

    @field_validator("phone")
    @classmethod
    def _phone(cls, v):
        return clean_phone(v)


class Track(BaseModel):
    number: str
    phone: str


class ProductIn(BaseModel):
    sku: str = Field(min_length=1, max_length=40)
    name: str = Field(min_length=2, max_length=160)
    brand: str = Field(default="", max_length=80)
    size: str = Field(default="", max_length=40)
    description: str = Field(default="", max_length=2000)
    category_id: Optional[int] = None
    supplier_id: Optional[int] = None
    price: Decimal = Field(ge=0, max_digits=12, decimal_places=2)
    cost: Decimal = Field(default=Decimal("0"), ge=0, max_digits=12, decimal_places=2)
    stock: int = Field(default=0, ge=0)
    reorder_level: int = Field(default=10, ge=0)
    badge: str = Field(default="", max_length=30)
    active: bool = True


class StockAdjust(BaseModel):
    change: int
    reason: str = Field(min_length=3, max_length=200)


class StatusChange(BaseModel):
    status: str
    note: str = Field(default="", max_length=300)


class PaymentChange(BaseModel):
    payment_status: str
    payment_ref: str = Field(default="", max_length=80)


class CategoryIn(BaseModel):
    name: str = Field(min_length=2, max_length=80)
    sort: int = 0


class SupplierIn(BaseModel):
    name: str = Field(min_length=2, max_length=120)
    contact: str = ""
    phone: str = ""
    email: str = ""
    country: str = "Ghana"
    lead_days: int = Field(default=7, ge=0, le=365)


class POLine(BaseModel):
    product_id: int
    quantity: int = Field(gt=0, le=100000)
    unit_cost: Decimal = Field(ge=0, max_digits=12, decimal_places=2)


class POIn(BaseModel):
    supplier_id: int
    note: str = ""
    lines: list[POLine] = Field(min_length=1)


class ZoneIn(BaseModel):
    name: str = Field(min_length=2, max_length=80)
    fee: Decimal = Field(ge=0, max_digits=12, decimal_places=2)
    eta: str = "Same day"
    active: bool = True


class SettingsIn(BaseModel):
    free_delivery_over: Decimal = Field(ge=0)
    store_name: str = Field(min_length=2, max_length=60)
    contact_phone: str = ""
    momo_instructions: str = Field(default="", max_length=400)


class StaffIn(BaseModel):
    name: str = Field(min_length=2, max_length=120)
    email: str
    password: str = Field(min_length=8, max_length=128)
    role: str = "staff"


class StaffUpdate(BaseModel):
    active: Optional[bool] = None
    role: Optional[str] = None
    password: Optional[str] = Field(default=None, min_length=8, max_length=128)
