"""
Health Check Tools for Autonomous Agent

Provides health monitoring capabilities for the data factory:
- Pipeline health status
- Data warehouse layer health
- System-wide health aggregation
- Recommendations generation
"""

from __future__ import annotations

import asyncio
from datetime import datetime, timezone
from typing import Any

import asyncpg

from ..types import (
    HealthStatus,
    PipelineHealth,
    LayerHealth,
    SystemHealth,
    FRESHNESS_THRESHOLDS,
    QUALITY_THRESHOLDS,
)


class HealthCheckTools:
    """
    Health check tools for the autonomous agent

    Token consumption: 0 (pure Python + SQL)
    """

    def __init__(self, db_url: str | None = None):
        self.db_url = db_url or "postgresql://sweetnight:sweetnight123@localhost:5433/sweetnight_geo"
        self._conn: asyncpg.Connection | None = None

    async def _get_connection(self) -> asyncpg.Connection:
        """Get database connection"""
        if self._conn is None or self._conn.is_closed():
            self._conn = await asyncpg.connect(self.db_url)
        return self._conn

    async def close(self):
        """Close database connection"""
        if self._conn and not self._conn.is_closed():
            await self._conn.close()
            self._conn = None

    # ========================================================================
    # Pipeline Health Checks
    # ========================================================================

    async def check_pipeline_health(self) -> list[PipelineHealth]:
        """
        Check health of all ETL pipelines

        Queries: ads.etl_pipeline_status view
        """
        conn = await self._get_connection()

        try:
            rows = await conn.fetch("""
                SELECT
                    module,
                    last_data_date,
                    hours_since_run,
                    status
                FROM ads.etl_pipeline_status
            """)

            pipelines = []
            for row in rows:
                hours = float(row['hours_since_run']) if row['hours_since_run'] else None

                # Determine health status
                if row['status'] == 'healthy':
                    status = HealthStatus.HEALTHY
                elif row['status'] == 'stale':
                    status = HealthStatus.STALE
                elif hours and hours > FRESHNESS_THRESHOLDS['critical']:
                    status = HealthStatus.CRITICAL
                elif hours and hours > FRESHNESS_THRESHOLDS['stale']:
                    status = HealthStatus.STALE
                elif hours and hours > FRESHNESS_THRESHOLDS['healthy']:
                    status = HealthStatus.DEGRADED
                else:
                    status = HealthStatus.HEALTHY

                pipelines.append(PipelineHealth(
                    module=row['module'],
                    last_data_date=str(row['last_data_date']) if row['last_data_date'] else None,
                    hours_since_run=hours,
                    status=status,
                ))

            return pipelines

        except Exception as e:
            # Return unknown status on error
            return [PipelineHealth(
                module="unknown",
                last_data_date=None,
                hours_since_run=None,
                status=HealthStatus.UNKNOWN,
                last_error=str(e),
            )]

    async def check_single_pipeline(self, module: str) -> PipelineHealth:
        """Check health of a specific pipeline"""
        conn = await self._get_connection()

        try:
            row = await conn.fetchrow("""
                SELECT
                    module,
                    last_data_date,
                    hours_since_run,
                    status
                FROM ads.etl_pipeline_status
                WHERE module = $1
            """, module)

            if not row:
                return PipelineHealth(
                    module=module,
                    last_data_date=None,
                    hours_since_run=None,
                    status=HealthStatus.UNKNOWN,
                    last_error=f"Pipeline {module} not found",
                )

            hours = float(row['hours_since_run']) if row['hours_since_run'] else None

            # Determine status
            if row['status'] == 'healthy':
                status = HealthStatus.HEALTHY
            elif row['status'] == 'stale':
                status = HealthStatus.STALE
            else:
                status = HealthStatus.UNKNOWN

            return PipelineHealth(
                module=row['module'],
                last_data_date=str(row['last_data_date']) if row['last_data_date'] else None,
                hours_since_run=hours,
                status=status,
            )

        except Exception as e:
            return PipelineHealth(
                module=module,
                last_data_date=None,
                hours_since_run=None,
                status=HealthStatus.UNKNOWN,
                last_error=str(e),
            )

    # ========================================================================
    # Layer Health Checks
    # ========================================================================

    async def check_layer_health(self) -> list[LayerHealth]:
        """
        Check health of all data warehouse layers

        Queries: pg_stat_user_tables for row counts
        """
        conn = await self._get_connection()

        try:
            rows = await conn.fetch("""
                SELECT
                    schemaname as layer,
                    COUNT(*) as tables,
                    COALESCE(SUM(n_live_tup), 0) as total_rows
                FROM pg_stat_user_tables
                WHERE schemaname IN ('ods', 'dwd', 'dws')
                GROUP BY schemaname
                ORDER BY CASE schemaname
                    WHEN 'ods' THEN 1
                    WHEN 'dwd' THEN 2
                    WHEN 'dws' THEN 3
                END
            """)

            layers = []
            for row in rows:
                layer_name = row['layer']
                total_rows = int(row['total_rows'])

                # Determine status based on row count
                issues = []
                if total_rows == 0:
                    status = HealthStatus.CRITICAL
                    issues.append(f"{layer_name} layer has no data")
                elif layer_name == 'dwd' and total_rows < 100:
                    status = HealthStatus.DEGRADED
                    issues.append(f"{layer_name} layer has very few records")
                else:
                    status = HealthStatus.HEALTHY

                layers.append(LayerHealth(
                    layer=layer_name,
                    tables_count=int(row['tables']),
                    total_rows=total_rows,
                    freshness_hours=None,  # Would need timestamp columns
                    status=status,
                    issues=issues,
                ))

            # Add ADS layer (views)
            ads_count = await conn.fetchval("""
                SELECT COUNT(*)
                FROM information_schema.views
                WHERE table_schema = 'ads'
            """)

            layers.append(LayerHealth(
                layer='ads',
                tables_count=int(ads_count) if ads_count else 0,
                total_rows=0,  # Views don't have row counts
                freshness_hours=None,
                status=HealthStatus.HEALTHY,
                issues=[],
            ))

            return layers

        except Exception as e:
            return [LayerHealth(
                layer="unknown",
                tables_count=0,
                total_rows=0,
                freshness_hours=None,
                status=HealthStatus.UNKNOWN,
                issues=[str(e)],
            )]

    # ========================================================================
    # System Health Aggregation
    # ========================================================================

    async def get_system_health(self) -> SystemHealth:
        """
        Get comprehensive system health report

        Aggregates pipeline and layer health into overall status
        """
        # Check pipelines and layers in parallel
        pipelines, layers = await asyncio.gather(
            self.check_pipeline_health(),
            self.check_layer_health(),
        )

        # Count health statuses
        all_statuses = [p.status for p in pipelines] + [l.status for l in layers]

        healthy_count = sum(1 for s in all_statuses if s == HealthStatus.HEALTHY)
        degraded_count = sum(1 for s in all_statuses if s in [HealthStatus.DEGRADED, HealthStatus.STALE])
        critical_count = sum(1 for s in all_statuses if s == HealthStatus.CRITICAL)

        # Determine overall status
        if critical_count > 0:
            overall_status = HealthStatus.CRITICAL
        elif degraded_count > 0:
            overall_status = HealthStatus.DEGRADED
        else:
            overall_status = HealthStatus.HEALTHY

        # Generate recommendations
        recommendations = self._generate_recommendations(pipelines, layers)

        return SystemHealth(
            pipelines=pipelines,
            layers=layers,
            overall_status=overall_status,
            healthy_count=healthy_count,
            degraded_count=degraded_count,
            critical_count=critical_count,
            recommendations=recommendations,
        )

    def _generate_recommendations(
        self,
        pipelines: list[PipelineHealth],
        layers: list[LayerHealth]
    ) -> list[str]:
        """Generate actionable recommendations based on health status"""
        recommendations = []

        # Check stale pipelines
        stale_pipelines = [p for p in pipelines if p.status in [HealthStatus.STALE, HealthStatus.CRITICAL]]
        for p in stale_pipelines:
            if p.status == HealthStatus.CRITICAL:
                recommendations.append(f"🚨 CRITICAL: Run /etl-run {p.module} immediately (data is {p.hours_since_run:.1f}h stale)")
            else:
                recommendations.append(f"⚠️ STALE: Consider running /etl-run {p.module} (data is {p.hours_since_run:.1f}h old)")

        # Check empty layers
        empty_layers = [l for l in layers if l.total_rows == 0 and l.layer != 'ads']
        for layer in empty_layers:
            recommendations.append(f"❌ EMPTY: {layer.layer.upper()} layer has no data - run /dw-etl to populate")

        # Check layer issues
        for layer in layers:
            for issue in layer.issues:
                if issue not in recommendations:
                    recommendations.append(f"⚠️ {layer.layer.upper()}: {issue}")

        if not recommendations:
            recommendations.append("✅ All systems healthy - no action required")

        return recommendations


# Tool definitions for Claude Agent SDK
HEALTH_CHECK_TOOLS = [
    {
        "name": "check_pipeline_health",
        "description": "Check health status of all ETL pipelines. Returns list of pipelines with their status (healthy/stale/critical), last data date, and hours since last run.",
        "input_schema": {
            "type": "object",
            "properties": {},
            "required": []
        }
    },
    {
        "name": "check_single_pipeline",
        "description": "Check health of a specific ETL pipeline by module name.",
        "input_schema": {
            "type": "object",
            "properties": {
                "module": {
                    "type": "string",
                    "description": "Pipeline module name (e.g., 'ga4', 'bsr', 'visibility')"
                }
            },
            "required": ["module"]
        }
    },
    {
        "name": "check_layer_health",
        "description": "Check health of data warehouse layers (ODS, DWD, DWS, ADS). Returns table counts and row counts per layer.",
        "input_schema": {
            "type": "object",
            "properties": {},
            "required": []
        }
    },
    {
        "name": "get_system_health",
        "description": "Get comprehensive system health report including all pipelines, layers, and actionable recommendations.",
        "input_schema": {
            "type": "object",
            "properties": {},
            "required": []
        }
    },
]
