import requests
import time
import os
import sys
from sqlalchemy import create_engine, ForeignKey, UniqueConstraint, Index
from sqlalchemy.orm import sessionmaker, relationship, DeclarativeBase, mapped_column, Mapped
from dataclasses import dataclass
from typing import List

class Base(DeclarativeBase):
    pass

@dataclass
class Host(Base):
    __tablename__ = 'hosts'
    id: Mapped[int] = mapped_column(primary_key=True)
    hostname: Mapped[str] = mapped_column(unique=True)
    current_stats: Mapped[List["CurrentStat"]] = relationship("CurrentStat", back_populates="host")
    historical_stats: Mapped[List["HistoricalStat"]] = relationship("HistoricalStat", back_populates="host")

@dataclass
class CurrentStat(Base):
    __tablename__ = 'current_stats'
    id: Mapped[int] = mapped_column(primary_key=True)
    host_id: Mapped[int] = mapped_column(ForeignKey('hosts.id'))
    category: Mapped[str] = mapped_column()
    name: Mapped[str] = mapped_column()
    value: Mapped[str] = mapped_column()
    unit: Mapped[str | None] = mapped_column(nullable=True)
    timestamp: Mapped[float] = mapped_column()
    host: Mapped["Host"] = relationship("Host", back_populates="current_stats")
    __table_args__ = (
        UniqueConstraint('host_id', 'category', 'name'),
        Index('idx_current_host', 'host_id'),
    )

@dataclass
class HistoricalStat(Base):
    __tablename__ = 'historical_stats'
    id: Mapped[int] = mapped_column(primary_key=True)
    host_id: Mapped[int] = mapped_column(ForeignKey('hosts.id'))
    category: Mapped[str] = mapped_column()
    name: Mapped[str] = mapped_column()
    value: Mapped[str] = mapped_column()
    unit: Mapped[str | None] = mapped_column(nullable=True)
    timestamp: Mapped[float] = mapped_column()
    host: Mapped["Host"] = relationship("Host", back_populates="historical_stats")
    __table_args__ = (
        Index('idx_historical_host', 'host_id'),
        Index('idx_historical_timestamp', 'timestamp'),
    )

class StatsCollector:
    def __init__(self, db_path="stats.db"):
        state_home = os.environ.get("XDG_STATE_HOME", os.path.expanduser("~/.local/state"))
        stats_dir = os.path.join(state_home, "stats")
        os.makedirs(stats_dir, exist_ok=True)
        self.db_path = os.path.join(stats_dir, db_path)
        if os.path.exists(self.db_path):
            os.remove(self.db_path)
        self.engine = create_engine(f'sqlite:///{self.db_path}')
        self.setup_database()
        Session = sessionmaker(bind=self.engine)
        self.session = Session()

    def setup_database(self):
        """Initialize the SQLite database schema"""
        Base.metadata.create_all(self.engine)

    def get_or_create_host_id(self, hostname):
        """Get host ID from database or create if not exists"""
        host = self.session.query(Host).filter_by(hostname=hostname).first()
        if not host:
            host = Host(hostname=hostname)
            self.session.add(host)
            self.session.commit()
        return host.id

    def store_stats(self, hostname, stats_data):
        """Store stats in the database"""
        host_id = self.get_or_create_host_id(hostname)
        timestamp = stats_data.get('timestamp', time.time())

        for category, category_stats in stats_data['stats'].items():
            for name, data in category_stats.items():
                value = data['value']
                unit = data.get('unit', None)
                is_historical = data.get('track', False)

                if is_historical:
                    hist_stat = HistoricalStat(
                        host_id=host_id,
                        category=category,
                        name=name,
                        value=value,
                        unit=unit,
                        timestamp=timestamp
                    )
                    self.session.add(hist_stat)
                else:
                    # Only update/create current stat if not historical
                    curr_stat = self.session.query(CurrentStat).filter_by(
                        host_id=host_id,
                        category=category,
                        name=name
                    ).first()

                    if curr_stat:
                        curr_stat.value = value
                        curr_stat.unit = unit
                        curr_stat.timestamp = timestamp
                    else:
                        curr_stat = CurrentStat(
                            host_id=host_id,
                            category=category,
                            name=name,
                            value=value,
                            unit=unit,
                            timestamp=timestamp
                        )
                        self.session.add(curr_stat)

        self.session.commit()

    def collect_from_host(self, hostname, port=5747):
        """Collect stats from a single host"""
        try:
            url = f"http://{hostname}:{port}"
            response = requests.get(url, timeout=5)
            response.raise_for_status()
            stats_data = response.json()
            self.store_stats(hostname, stats_data)
            print(f"Successfully collected stats from {hostname}")
            return True
        except Exception as e:
            print(f"Error collecting stats from {hostname}: {str(e)}", file=sys.stderr)
            return False
