from app_server.extensions import db
from app_server.models import User
from app_server.utils.otp_service import (
    issue_otp,
    is_contact_verified,
    send_email_otp,
    verify_issued_otp,
)
from app_server.utils.security import (
    hash_password,
    is_valid_email,
    is_valid_phone,
    normalize_email,
    normalize_phone,
    username_starts_with_letter,
)


def registration_payload(data):
    if hasattr(data, "get"):
        raw_password = data.get("password")
        password = "" if raw_password is None else str(raw_password)
        return {
            "username": (data.get("username") or "").strip(),
            "email": normalize_email(data.get("email")),
            "phone": normalize_phone(data.get("phone") or ""),
            "password": password,
            "address": (data.get("address") or "").strip(),
        }
    raise TypeError("Unsupported registration payload source.")


def validate_registration_payload(payload):
    if len(payload["username"]) < 3:
        return "Username must be at least 3 characters."
    if not username_starts_with_letter(payload["username"]):
        return "Username must start with a letter (A–Z)."
    if not payload["email"]:
        return "Email is required."
    if not is_valid_email(payload["email"]):
        return "Enter a valid email address."
    if payload["phone"] and not is_valid_phone(payload["phone"]):
        return "Enter a valid mobile number."
    if len(payload["password"]) < 8:
        return "Password must be at least 8 characters."
    if User.query.filter_by(username=payload["username"]).first():
        return "Username already taken."
    if User.query.filter_by(email=payload["email"]).first():
        return "Email already registered."
    if payload["phone"] and User.query.filter_by(phone=payload["phone"]).first():
        return "Mobile number already registered."
    return None


def registration_pending_snapshot(payload):
    return {
        "username": payload["username"],
        "email": payload["email"],
        "phone": payload["phone"],
        "password": payload["password"],
        "address": payload["address"],
    }


def send_registration_otps(payload):
    email_code, _ = issue_otp("email", payload["email"], "registration")
    if not send_email_otp(payload["email"], email_code):
        return False, "Could not send email verification code."

    snap = registration_pending_snapshot(payload)
    return True, snap


def registration_pending_matches(pending, payload):
    if not pending:
        return False, "Request verification codes before registering."
    if payload["username"] != pending["username"] or payload["email"] != pending["email"]:
        return False, "Registration details changed after codes were sent."
    if payload["phone"] != (pending.get("phone") or ""):
        return False, "Registration details changed after codes were sent."
    return True, ""


def verify_registration_otps(pending, payload, email_otp):
    if payload["email"]:
        if not email_otp:
            return False, "Enter the email verification code."
        email_ok, email_message = verify_issued_otp(
            "email", pending["email"], "registration", email_otp
        )
        if not email_ok:
            return False, email_message

    return True, ""


def assert_registration_contacts_verified(payload):
    if payload["email"] and not is_contact_verified("email", payload["email"]):
        return False, "Email must be verified before it can be added to your account."
    return True, ""


def create_verified_customer(payload):
    verified, message = assert_registration_contacts_verified(payload)
    if not verified:
        return None, message

    user = User(
        username=payload["username"],
        email=payload["email"],
        password=hash_password(payload["password"]),
        phone=payload["phone"] or None,
        address=payload["address"] or None,
        role="customer",
    )
    db.session.add(user)
    db.session.commit()
    return user, "Account created."
