# @File: backend/models/uptimerobot.py
# @Desc: SQLAlchemy ORM models for the UptimeRobot Reseller Manager module.
#        All client monitors/contacts/windows/pages are created under DoItEasy's
#        single UptimeRobot Team plan; ownership back to a DoItEasy user (and
#        optional WHMCS service id) is tracked here. Usage snapshots power the
#        Team-plan consumption dashboard and upgrade alerts.
#
# Conventions:
#   - `created_at` / `updated_at` are managed by the DB (server_default=now()).
#   - Each table is idempotent in the matching Alembic migration so repeated
#     bootstraps never fail.
#   - `encrypted_*` fields are never stored here — secret material (the
#     UptimeRobot master API key) lives in `api_hub_credentials` under
#     provider_id="uptimerobot".
from __future__ import annotations

from core.database import Base
from sqlalchemy import (
    Boolean,
    Column,
    DateTime,
    Index,
    Integer,
    String,
    Text,
    UniqueConstraint,
)
from sqlalchemy.sql import func


class UptimePackage(Base):
    """An admin-defined monitoring package/plan sold to clients."""

    __tablename__ = "uptime_packages"
    __table_args__ = (
        UniqueConstraint("slug", name="uq_uptime_packages_slug"),
        {"extend_existing": True},
    )

    id = Column(Integer, primary_key=True, index=True, autoincrement=True)
    slug = Column(String(64), nullable=False, index=True)
    name = Column(String(128), nullable=False)
    description = Column(Text, nullable=True)

    # Quotas — `-1` or the `*_unlimited` flag means "no cap".
    max_monitors = Column(Integer, nullable=False, default=0)
    monitors_unlimited = Column(Boolean, nullable=False, default=False)
    max_alert_contacts = Column(Integer, nullable=False, default=0)
    alert_contacts_unlimited = Column(Boolean, nullable=False, default=False)
    max_maintenance_windows = Column(Integer, nullable=False, default=0)
    maintenance_windows_unlimited = Column(Boolean, nullable=False, default=False)
    max_status_pages = Column(Integer, nullable=False, default=0)
    status_pages_unlimited = Column(Boolean, nullable=False, default=False)

    # Minimum check interval the plan allows (seconds). UptimeRobot v3 uses
    # seconds; the default paid minimum is 60s.
    min_check_interval = Column(Integer, nullable=False, default=300)

    # Comma-separated list of allowed monitor types, e.g. "http,keyword,ping,port,heartbeat".
    allowed_monitor_types = Column(String(255), nullable=False, default="http")

    # Feature flags.
    public_status_page_allowed = Column(Boolean, nullable=False, default=False)
    api_monitoring_allowed = Column(Boolean, nullable=False, default=False)
    advanced_notifications_allowed = Column(Boolean, nullable=False, default=False)

    # Optional mapping to a WHMCS product id so the provisioning hook can
    # resolve this package automatically on product activation.
    whmcs_product_id = Column(Integer, nullable=True, index=True)

    # What to do when a client service is terminated in WHMCS — either
    # "delete" the upstream monitors or "archive" them (soft stop only).
    terminate_action = Column(String(16), nullable=False, default="archive")

    is_active = Column(Boolean, nullable=False, default=True)
    price_per_month = Column(String(32), nullable=True)  # display-only; actual pricing in WHMCS

    created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
    updated_at = Column(
        DateTime(timezone=True),
        server_default=func.now(),
        onupdate=func.now(),
        nullable=False,
    )


class UptimeClientSubscription(Base):
    """Ties a DoItEasy user (optionally with a WHMCS service id) to an
    :class:`UptimePackage`. One user may have multiple subscriptions."""

    __tablename__ = "uptime_client_subscriptions"
    __table_args__ = (
        Index("ix_uptime_subs_user_id", "user_id"),
        Index("ix_uptime_subs_status", "status"),
        {"extend_existing": True},
    )

    id = Column(Integer, primary_key=True, index=True, autoincrement=True)
    user_id = Column(String(255), nullable=False)
    user_email = Column(String(255), nullable=True, index=True)

    package_id = Column(Integer, nullable=False, index=True)
    # WHMCS service id + product id for traceability from the provisioning
    # side. Either may be null for manually-assigned subscriptions.
    whmcs_service_id = Column(Integer, nullable=True, index=True)
    whmcs_product_id = Column(Integer, nullable=True)

    # active / suspended / terminated
    status = Column(String(16), nullable=False, default="active")
    # Cached usage counters refreshed on every write path and by the cron.
    monitors_used = Column(Integer, nullable=False, default=0)
    alert_contacts_used = Column(Integer, nullable=False, default=0)
    maintenance_windows_used = Column(Integer, nullable=False, default=0)
    status_pages_used = Column(Integer, nullable=False, default=0)

    notes = Column(Text, nullable=True)
    activated_at = Column(DateTime(timezone=True), nullable=True)
    suspended_at = Column(DateTime(timezone=True), nullable=True)
    terminated_at = Column(DateTime(timezone=True), nullable=True)

    # When True, the owner of this subscription receives an email every time
    # one of their monitors transitions between up / down / seems_down. The
    # toggle is exposed on /client/uptime.
    email_notifications_enabled = Column(Boolean, nullable=False, default=False)

    created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
    updated_at = Column(
        DateTime(timezone=True),
        server_default=func.now(),
        onupdate=func.now(),
        nullable=False,
    )


class UptimeClientMonitor(Base):
    """Internal mirror of a monitor that lives on the upstream UptimeRobot
    Team account. The `ur_monitor_id` is the authoritative foreign key."""

    __tablename__ = "uptime_client_monitors"
    __table_args__ = (
        UniqueConstraint("ur_monitor_id", name="uq_uptime_monitors_ur_id"),
        Index("ix_uptime_monitors_subscription", "subscription_id"),
        Index("ix_uptime_monitors_user", "user_id"),
        {"extend_existing": True},
    )

    id = Column(Integer, primary_key=True, index=True, autoincrement=True)
    subscription_id = Column(Integer, nullable=False)
    user_id = Column(String(255), nullable=False)

    ur_monitor_id = Column(String(64), nullable=False)
    friendly_name = Column(String(255), nullable=False)
    # http | ping | port | keyword | heartbeat
    monitor_type = Column(String(32), nullable=False)
    url = Column(Text, nullable=True)
    keyword = Column(String(255), nullable=True)
    port = Column(Integer, nullable=True)
    interval_seconds = Column(Integer, nullable=False, default=300)

    # up | down | paused | unknown | seems_down
    status = Column(String(32), nullable=False, default="unknown")
    uptime_ratio_24h = Column(String(16), nullable=True)
    uptime_ratio_7d = Column(String(16), nullable=True)
    uptime_ratio_30d = Column(String(16), nullable=True)
    response_time_ms = Column(Integer, nullable=True)

    last_error = Column(Text, nullable=True)
    last_synced_at = Column(DateTime(timezone=True), nullable=True)
    paused = Column(Boolean, nullable=False, default=False)
    archived = Column(Boolean, nullable=False, default=False)

    created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
    updated_at = Column(
        DateTime(timezone=True),
        server_default=func.now(),
        onupdate=func.now(),
        nullable=False,
    )


class UptimeAlertContact(Base):
    """An alert contact (email / sms / webhook) owned by a client subscription."""

    __tablename__ = "uptime_alert_contacts"
    __table_args__ = (
        UniqueConstraint("ur_contact_id", name="uq_uptime_contacts_ur_id"),
        Index("ix_uptime_contacts_subscription", "subscription_id"),
        {"extend_existing": True},
    )

    id = Column(Integer, primary_key=True, index=True, autoincrement=True)
    subscription_id = Column(Integer, nullable=False)
    user_id = Column(String(255), nullable=False)

    ur_contact_id = Column(String(64), nullable=False)
    contact_type = Column(String(32), nullable=False)  # email, sms, webhook, slack, telegram
    friendly_name = Column(String(128), nullable=True)
    value = Column(String(512), nullable=False)
    verified = Column(Boolean, nullable=False, default=False)

    created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
    updated_at = Column(
        DateTime(timezone=True),
        server_default=func.now(),
        onupdate=func.now(),
        nullable=False,
    )


class UptimeMaintenanceWindow(Base):
    """A maintenance window owned by a client subscription."""

    __tablename__ = "uptime_maintenance_windows"
    __table_args__ = (
        UniqueConstraint("ur_window_id", name="uq_uptime_windows_ur_id"),
        Index("ix_uptime_windows_subscription", "subscription_id"),
        {"extend_existing": True},
    )

    id = Column(Integer, primary_key=True, index=True, autoincrement=True)
    subscription_id = Column(Integer, nullable=False)
    user_id = Column(String(255), nullable=False)

    ur_window_id = Column(String(64), nullable=False)
    friendly_name = Column(String(128), nullable=False)
    window_type = Column(String(16), nullable=False, default="once")  # once | daily | weekly | monthly
    start_time = Column(String(64), nullable=True)
    duration_minutes = Column(Integer, nullable=False, default=60)
    status = Column(String(16), nullable=False, default="active")  # active | paused

    created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
    updated_at = Column(
        DateTime(timezone=True),
        server_default=func.now(),
        onupdate=func.now(),
        nullable=False,
    )


class UptimeStatusPage(Base):
    """A public status page owned by a client subscription."""

    __tablename__ = "uptime_status_pages"
    __table_args__ = (
        UniqueConstraint("ur_page_id", name="uq_uptime_pages_ur_id"),
        Index("ix_uptime_pages_subscription", "subscription_id"),
        {"extend_existing": True},
    )

    id = Column(Integer, primary_key=True, index=True, autoincrement=True)
    subscription_id = Column(Integer, nullable=False)
    user_id = Column(String(255), nullable=False)

    ur_page_id = Column(String(64), nullable=False)
    friendly_name = Column(String(128), nullable=False)
    standard_url = Column(String(512), nullable=True)
    custom_url = Column(String(512), nullable=True)
    # JSON blob describing which monitor ids are included (kept as TEXT for
    # portability across SQLite/Postgres).
    monitor_ids_json = Column(Text, nullable=True)

    created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
    updated_at = Column(
        DateTime(timezone=True),
        server_default=func.now(),
        onupdate=func.now(),
        nullable=False,
    )


class UptimeApiLog(Base):
    """One row per outbound UptimeRobot API call. Used by the diagnostics page."""

    __tablename__ = "uptime_api_logs"
    __table_args__ = (
        Index("ix_uptime_api_logs_created", "created_at"),
        Index("ix_uptime_api_logs_status", "status"),
        {"extend_existing": True},
    )

    id = Column(Integer, primary_key=True, index=True, autoincrement=True)
    endpoint = Column(String(255), nullable=False)
    method = Column(String(8), nullable=False, default="GET")
    status = Column(String(16), nullable=False, default="ok")  # ok | error
    error_class = Column(String(64), nullable=True)
    http_status = Column(Integer, nullable=True)
    duration_ms = Column(Integer, nullable=True)
    user_email = Column(String(255), nullable=True)
    error_message = Column(Text, nullable=True)
    created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)


class UptimeUsageSnapshot(Base):
    """A point-in-time snapshot of global Team-plan consumption."""

    __tablename__ = "uptime_usage_snapshots"
    __table_args__ = (
        Index("ix_uptime_usage_created", "created_at"),
        {"extend_existing": True},
    )

    id = Column(Integer, primary_key=True, index=True, autoincrement=True)
    monitors_used = Column(Integer, nullable=False, default=0)
    monitors_total = Column(Integer, nullable=True)
    alert_contacts_used = Column(Integer, nullable=False, default=0)
    alert_contacts_total = Column(Integer, nullable=True)
    status_pages_used = Column(Integer, nullable=False, default=0)
    status_pages_total = Column(Integer, nullable=True)
    maintenance_windows_used = Column(Integer, nullable=False, default=0)
    maintenance_windows_total = Column(Integer, nullable=True)
    plan_name = Column(String(64), nullable=True)
    # ok | warn_70 | warn_85 | critical_95 | upgrade_required
    warning_state = Column(String(32), nullable=False, default="ok")
    created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)