from datetime import datetime, date
from typing import Optional

from sqlalchemy import (
    Boolean,
    Date,
    DateTime,
    ForeignKey,
    Integer,
    String,
    Text,
    func,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship

from database import Base


# ---------------------------------------------------------------------------
# RegistrarAccount
# ---------------------------------------------------------------------------


class RegistrarAccount(Base):
    __tablename__ = "registrar_accounts"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    registrar: Mapped[str] = mapped_column(
        String(64), nullable=False
    )  # 'namecheap'|'godaddy'|'cloudflare'|'spaceship'
    credentials_encrypted: Mapped[str] = mapped_column(Text, nullable=False)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), nullable=False
    )

    # Relationships
    domains: Mapped[list["Domain"]] = relationship(
        "Domain", back_populates="account", cascade="all, delete-orphan"
    )

    def __repr__(self) -> str:
        return f"<RegistrarAccount id={self.id} name={self.name!r} registrar={self.registrar!r}>"


# ---------------------------------------------------------------------------
# Domain
# ---------------------------------------------------------------------------


class Domain(Base):
    __tablename__ = "domains"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
    account_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("registrar_accounts.id", ondelete="CASCADE"), nullable=False
    )
    domain_name: Mapped[str] = mapped_column(String(255), nullable=False, index=True)
    expiry_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
    auto_renew: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    status: Mapped[str] = mapped_column(
        String(32), default="unknown", nullable=False
    )  # 'active'|'expired'|'locked'|'unknown'
    registrar_domain_id: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    last_synced: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True), nullable=True)

    # Relationships
    account: Mapped["RegistrarAccount"] = relationship("RegistrarAccount", back_populates="domains")
    dns_records: Mapped[list["DnsRecord"]] = relationship(
        "DnsRecord", back_populates="domain", cascade="all, delete-orphan"
    )
    dns_checks: Mapped[list["DnsCheckResult"]] = relationship(
        "DnsCheckResult", back_populates="domain", cascade="all, delete-orphan"
    )
    notifications: Mapped[list["Notification"]] = relationship(
        "Notification", back_populates="domain", cascade="all, delete-orphan"
    )

    def __repr__(self) -> str:
        return f"<Domain id={self.id} name={self.domain_name!r} status={self.status!r}>"


# ---------------------------------------------------------------------------
# DnsRecord (snapshot history)
# ---------------------------------------------------------------------------


class DnsRecord(Base):
    __tablename__ = "dns_records"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
    domain_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("domains.id", ondelete="CASCADE"), nullable=False, index=True
    )
    record_type: Mapped[str] = mapped_column(
        String(16), nullable=False
    )  # A, AAAA, CNAME, MX, TXT, NS, SOA
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    value: Mapped[str] = mapped_column(Text, nullable=False)
    ttl: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
    priority: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)  # For MX
    snapshotted_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), nullable=False, index=True
    )

    # Relationships
    domain: Mapped["Domain"] = relationship("Domain", back_populates="dns_records")

    def __repr__(self) -> str:
        return f"<DnsRecord id={self.id} type={self.record_type!r} name={self.name!r}>"


# ---------------------------------------------------------------------------
# DnsCheckResult (live check results)
# ---------------------------------------------------------------------------


class DnsCheckResult(Base):
    __tablename__ = "dns_check_results"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
    domain_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("domains.id", ondelete="CASCADE"), nullable=False, index=True
    )
    checked_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), nullable=False, index=True
    )
    resolver_used: Mapped[str] = mapped_column(String(64), nullable=False)
    record_type: Mapped[str] = mapped_column(String(16), nullable=False)
    resolved_values: Mapped[Optional[str]] = mapped_column(Text, nullable=True)  # JSON array
    is_reachable: Mapped[bool] = mapped_column(Boolean, nullable=False)
    response_time_ms: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
    error_message: Mapped[Optional[str]] = mapped_column(Text, nullable=True)

    # Relationships
    domain: Mapped["Domain"] = relationship("Domain", back_populates="dns_checks")

    def __repr__(self) -> str:
        return (
            f"<DnsCheckResult id={self.id} domain_id={self.domain_id} "
            f"type={self.record_type!r} reachable={self.is_reachable}>"
        )


# ---------------------------------------------------------------------------
# Notification
# ---------------------------------------------------------------------------


class Notification(Base):
    __tablename__ = "notifications"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
    domain_id: Mapped[int] = mapped_column(
        Integer, ForeignKey("domains.id", ondelete="CASCADE"), nullable=False, index=True
    )
    notification_type: Mapped[str] = mapped_column(
        String(32), nullable=False
    )  # 'expiry_30d'|'expiry_7d'|'expiry_1d'
    sent_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), nullable=False
    )
    recipient_email: Mapped[str] = mapped_column(String(255), nullable=False)

    # Relationships
    domain: Mapped["Domain"] = relationship("Domain", back_populates="notifications")

    def __repr__(self) -> str:
        return (
            f"<Notification id={self.id} domain_id={self.domain_id} "
            f"type={self.notification_type!r}>"
        )


# ---------------------------------------------------------------------------
# AppSettings (singleton row with id=1)
# ---------------------------------------------------------------------------


class AppSettings(Base):
    __tablename__ = "app_settings"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, default=1)
    smtp_host: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    smtp_port: Mapped[int] = mapped_column(Integer, default=587, nullable=False)
    smtp_user: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    smtp_password_encrypted: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    smtp_from: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    alert_emails: Mapped[Optional[str]] = mapped_column(Text, nullable=True)  # comma-separated
    expiry_alert_days: Mapped[str] = mapped_column(
        String(64), default="30,7,1", nullable=False
    )  # comma-separated

    def __repr__(self) -> str:
        return f"<AppSettings id={self.id} smtp_host={self.smtp_host!r}>"


# ---------------------------------------------------------------------------
# HostingService
# ---------------------------------------------------------------------------


class HostingService(Base):
    __tablename__ = "hosting_services"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    provider: Mapped[str] = mapped_column(
        String(64), default="cPanel", nullable=False
    )  # 'cPanel'|'Hetzner'|'AWS'|'DigitalOcean'|'Hostinger'|'Vercel'|'Other'
    ip_address: Mapped[Optional[str]] = mapped_column(String(64), nullable=True)
    server_hostname: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
    login_url: Mapped[Optional[str]] = mapped_column(String(512), nullable=True)
    expiry_date: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
    billing_cycle: Mapped[str] = mapped_column(
        String(32), default="yearly", nullable=False
    )  # 'monthly'|'yearly'|'quarterly'
    cost: Mapped[Optional[int]] = mapped_column(Integer, nullable=True)
    currency: Mapped[str] = mapped_column(String(8), default="USD", nullable=False)
    auto_renew: Mapped[bool] = mapped_column(Boolean, default=False, nullable=False)
    status: Mapped[str] = mapped_column(
        String(32), default="active", nullable=False
    )  # 'active'|'expiring_soon'|'expired'|'suspended'
    notes_encrypted: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), nullable=False
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False
    )

    def __repr__(self) -> str:
        return f"<HostingService id={self.id} name={self.name!r} provider={self.provider!r}>"


# ---------------------------------------------------------------------------
# User (Users & Role Management)
# ---------------------------------------------------------------------------


class User(Base):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
    username: Mapped[str] = mapped_column(String(64), unique=True, nullable=False, index=True)
    email: Mapped[str] = mapped_column(String(255), unique=True, nullable=False, index=True)
    password_hash: Mapped[str] = mapped_column(String(255), nullable=False)
    role: Mapped[str] = mapped_column(
        String(32), default="developer", nullable=False
    )  # 'admin' | 'developer' | 'user'
    custom_role_name: Mapped[Optional[str]] = mapped_column(String(64), nullable=True)
    permissions: Mapped[str] = mapped_column(
        Text, default="domains,cloudflare,hosting,dns,accounts,users,settings", nullable=False
    )
    is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), nullable=False
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), server_default=func.now(), onupdate=func.now(), nullable=False
    )

    def __repr__(self) -> str:
        return f"<User id={self.id} username={self.username!r} role={self.role!r}>"
