"""Instance model for tracking Preloop installations.

This model is used by both OSS and Enterprise editions to track
installation metadata. For self-hosted instances, this is a singleton
table containing the local instance's information.
"""

import uuid
from datetime import datetime, timezone
from typing import Any, Dict, Optional

from sqlalchemy import Float, String, Boolean, JSON
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.types import DateTime

from .base import Base


class Instance(Base):
    """Tracks Preloop instance information.

    For self-hosted (non-SaaS) installations, this table contains a single
    row representing the local instance. For SaaS (preloop.ai), this tracks
    all instances that check in for version updates.

    Attributes:
        id: Database primary key
        instance_uuid: Unique identifier generated by the instance on first run
        version: Current version of the instance
        edition: 'oss' or 'enterprise'
        ip_address: Last known IP address (for SaaS tracking)
        first_seen: When the instance was first created/seen
        last_seen: Most recent activity
        check_count: Number of times instance has checked in (SaaS)
        country: Country from IP geolocation (SaaS)
        country_code: ISO country code (SaaS)
        city: City from IP geolocation (SaaS)
        region: Region from IP geolocation (SaaS)
        lat: Latitude (SaaS)
        lon: Longitude (SaaS)
        is_active: Whether instance is considered active
        metadata: Additional instance metadata (features enabled, user count, etc.)
    """

    __tablename__ = "instances"

    # Core identity
    instance_uuid: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        unique=True,
        nullable=False,
        index=True,
        comment="Unique identifier generated by the instance",
    )
    version: Mapped[str] = mapped_column(
        String(50), nullable=False, comment="Current version of the instance"
    )
    edition: Mapped[str] = mapped_column(
        String(20),
        nullable=False,
        default="oss",
        comment="Edition: 'oss' or 'enterprise'",
    )

    # Network info (primarily for SaaS tracking)
    ip_address: Mapped[Optional[str]] = mapped_column(
        String(45), nullable=True, comment="Last known IP address (IPv4 or IPv6)"
    )

    # Timestamps
    first_seen: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        nullable=False,
        default=lambda: datetime.now(timezone.utc),
        comment="When the instance was first created/seen",
    )
    last_seen: Mapped[datetime] = mapped_column(
        DateTime(timezone=True),
        nullable=False,
        default=lambda: datetime.now(timezone.utc),
        index=True,
        comment="Most recent activity",
    )
    check_count: Mapped[str] = mapped_column(
        String(20),
        nullable=False,
        default="1",
        comment="Number of check-ins (string for large numbers)",
    )

    # Geolocation (primarily for SaaS tracking)
    country: Mapped[Optional[str]] = mapped_column(
        String(100), nullable=True, comment="Country from IP geolocation"
    )
    country_code: Mapped[Optional[str]] = mapped_column(
        String(3), nullable=True, comment="ISO country code"
    )
    city: Mapped[Optional[str]] = mapped_column(
        String(100), nullable=True, comment="City from IP geolocation"
    )
    region: Mapped[Optional[str]] = mapped_column(
        String(100), nullable=True, comment="Region from IP geolocation"
    )
    lat: Mapped[Optional[float]] = mapped_column(
        Float, nullable=True, comment="Latitude"
    )
    lon: Mapped[Optional[float]] = mapped_column(
        Float, nullable=True, comment="Longitude"
    )

    # Status
    is_active: Mapped[bool] = mapped_column(
        Boolean,
        nullable=False,
        default=True,
        index=True,
        comment="Whether instance is considered active",
    )

    # Additional metadata
    metadata_: Mapped[Optional[Dict[str, Any]]] = mapped_column(
        "metadata",
        JSON,
        nullable=True,
        comment="Additional instance metadata (features, user count, etc.)",
    )

    def __repr__(self) -> str:
        return f"<Instance {self.instance_uuid} v{self.version} ({self.edition})>"

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary for API responses."""
        return {
            "id": str(self.id),
            "instance_uuid": str(self.instance_uuid),
            "version": self.version,
            "edition": self.edition,
            "ip_address": self.ip_address,
            "first_seen": self.first_seen.isoformat() if self.first_seen else None,
            "last_seen": self.last_seen.isoformat() if self.last_seen else None,
            "check_count": int(self.check_count) if self.check_count else 0,
            "country": self.country,
            "country_code": self.country_code,
            "city": self.city,
            "region": self.region,
            "lat": self.lat,
            "lon": self.lon,
            "is_active": self.is_active,
            "metadata": self.metadata_,
            "created_at": self.created_at.isoformat() if self.created_at else None,
            "updated_at": self.updated_at.isoformat() if self.updated_at else None,
        }

    def increment_check_count(self) -> None:
        """Increment the check count and update last_seen."""
        current = int(self.check_count) if self.check_count else 0
        self.check_count = str(current + 1)
        self.last_seen = datetime.now(timezone.utc)
