"""Cloud fallback path — resolves disputed or hard shards via a trusted cloud provider."""

from __future__ import annotations

import json

import structlog

from hive_compute.coordinator.database import get_db_ctx, record_event
from hive_compute.coordinator.metrics import increment_shard_completed, observe_shard_processing_duration

logger = structlog.get_logger()

# Fallback triggers — validation failures from settings, worker attempts from DB schema
def _get_max_validation_failures() -> int:
    from hive_compute.config import get_settings
    return get_settings().validated_fallback_max_failures()


def should_fallback(shard_id: str) -> bool:
    """Decide if a shard should be routed to the cloud fallback path.

    Triggers:
    - Shard has failed max worker attempts
    - Multiple validation disagreements on the shard
    - Canary failure on this shard's worker
    """
    with get_db_ctx() as db:
        shard = db.execute(
            "SELECT * FROM shards WHERE shard_id = ?",
            (shard_id,),
        ).fetchone()

        if not shard:
            return False

        # Already exhausted worker attempts
        if shard["status"] == "failed" and shard["attempts"] >= shard["max_attempts"]:
            return True

        # Check validation failures for this shard
        fail_count = db.execute(
            "SELECT COUNT(*) as cnt FROM validation_results WHERE shard_id = ? AND passed = 0",
            (shard_id,),
        ).fetchone()["cnt"]

        if fail_count >= _get_max_validation_failures():
            return True

    return False


def create_fallback_attempt(shard_id: str) -> dict | None:
    """Create a fallback attempt for a shard, routing it to a cloud provider.

    The fallback looks like another shard attempt but is tagged as 'fallback'
    and handled by a trusted cloud transcription service instead of a worker.
    """
    with get_db_ctx() as db:
        shard = db.execute(
            "SELECT * FROM shards WHERE shard_id = ?",
            (shard_id,),
        ).fetchone()

        if not shard:
            return None

        job_id = shard["job_id"]

        # Mark shard for fallback processing (claimed_by stays NULL — no worker owns it)
        db.execute(
            """
            UPDATE shards SET status = 'fallback', claimed_by = NULL,
                              claimed_at = datetime('now'), last_heartbeat = datetime('now')
            WHERE shard_id = ?
            """,
            (shard_id,),
        )

        record_event(
            db, job_id, "fallback_triggered",
            from_status=shard["status"], to_status="fallback",
            actor="fallback_system",
            detail=f"shard {shard['shard_index']} ({shard_id}) routed to cloud fallback",
        )

        job = db.execute(
            "SELECT audio_url, input_config FROM jobs WHERE job_id = ?",
            (job_id,),
        ).fetchone()

    logger.info("Fallback triggered", shard_id=shard_id, job_id=job_id)

    return {
        "shard_id": shard_id,
        "job_id": job_id,
        "audio_url": job["audio_url"] if job else None,
        "start_seconds": shard["start_seconds"],
        "end_seconds": shard["end_seconds"],
        "input_config": json.loads(job["input_config"]) if job and job["input_config"] else {},
    }


def complete_fallback(shard_id: str, transcript: str, provider: str = "cloud") -> dict:
    """Record the result of a cloud fallback transcription.

    This is treated like a high-trust result that wins over worker results.
    """
    result_data = json.dumps({
        "transcript": transcript,
        "model": "cloud-fallback",
        "backend": provider,
        "language": "en",
        "word_count": len(transcript.split()) if transcript else 0,
        "fallback": True,
    })

    with get_db_ctx() as db:
        shard = db.execute(
            "SELECT * FROM shards WHERE shard_id = ? AND status = 'fallback'",
            (shard_id,),
        ).fetchone()

        if not shard:
            return {"error": "Shard not found or not in fallback state"}

        job_id = shard["job_id"]

        db.execute(
            """
            UPDATE shards SET status = 'completed', result_data = ?,
                              completed_at = datetime('now')
            WHERE shard_id = ?
            """,
            (result_data, shard_id),
        )

        record_event(
            db, job_id, "fallback_completed",
            from_status="fallback", to_status="completed",
            actor="cloud-fallback",
            detail=f"shard {shard['shard_index']} resolved by {provider}",
        )
        completed_at = db.execute(
            "SELECT completed_at FROM shards WHERE shard_id = ?",
            (shard_id,),
        ).fetchone()["completed_at"]
        increment_shard_completed()
        observe_shard_processing_duration(shard["claimed_at"], completed_at)

        # Record fallback cost in validation_results for accounting
        db.execute(
            """
            INSERT INTO validation_results
                (shard_id, job_id, worker_id, validation_type, actual_text,
                 similarity_score, passed, detail)
            VALUES (?, ?, 'cloud-fallback', 'fallback', ?, 1.0, 1, ?)
            """,
            (shard_id, job_id, transcript[:500], f"provider={provider}"),
        )

    logger.info("Fallback completed", shard_id=shard_id, job_id=job_id, provider=provider)

    return {
        "shard_id": shard_id,
        "job_id": job_id,
        "status": "completed",
        "provider": provider,
    }


def get_fallback_stats() -> dict:
    """Get statistics on fallback usage."""
    with get_db_ctx() as db:
        total = db.execute(
            "SELECT COUNT(*) as cnt FROM validation_results WHERE validation_type = 'fallback'"
        ).fetchone()["cnt"]

        by_reason = db.execute(
            """
            SELECT detail, COUNT(*) as cnt FROM job_events
            WHERE event_type = 'fallback_triggered'
            GROUP BY detail
            """
        ).fetchall()

    return {
        "total_fallbacks": total,
        "by_reason": [{"detail": r["detail"], "count": r["cnt"]} for r in by_reason],
    }


def check_and_trigger_fallback(shard_id: str) -> dict | None:
    """Check if a shard needs fallback and trigger it if so.

    Called after shard failures or validation disagreements.
    Returns fallback info if triggered, None otherwise.
    """
    if should_fallback(shard_id):
        return create_fallback_attempt(shard_id)
    return None
