import database as db
import sqlite3
from datetime import datetime, timezone
import random
import asyncio
import time
from .sim_config import ALARM_TEXTS, TICKET_TEXTS, ASSIGNEES, SITE_IDS
from logger import get_logger
logger = get_logger(__name__)

next_alarm_cleanup_at: float = 0.0
next_incident_cleanup_at: float = 0.0

# TODO: Simulate weather / natural disasters

async def ticket_simulator(interval=60):
    # Generates tickets off of alarming sites.
    while True:
        conn = db.connect_to_db()
        try:
            logger.debug("Simulating tickets...")
            cur = conn.cursor()
            now = int(datetime.now(timezone.utc).timestamp())

            # Find sites that have unassigned simulator alarms. Will be used to assigned to bot
            rows = cur.execute("""
                SELECT DISTINCT site_id FROM alarms
                WHERE incident_id IS NULL AND created_by = 'simulator'
            """).fetchall()

            if not rows:
                logger.debug("No unassigned alarms — skipping ticket creation.")
                conn.close()
                await asyncio.sleep(interval)
                continue

            site_id = random.choice(rows)[0]
            text = random.choice(TICKET_TEXTS)
            assigned_to = random.choice(ASSIGNEES)

            # NOTE: Get the worse severity alarm that isn't assigned out.
            worst = cur.execute("""
                SELECT MIN(severity) FROM alarms
                WHERE site_id = ? AND incident_id IS NULL
            """, (site_id,)).fetchone()[0]
            severity = worst or random.randint(1, 5)

            cur.execute("""
                INSERT INTO incidents (text, severity, site_id, created, updated, assigned_to, status, created_by)
                VALUES (?, ?, ?, ?, ?, ?, 'active', 'simulator')
            """, (text, severity, site_id, now, now, assigned_to))
            incident_id = cur.lastrowid

            cur.execute("""
                UPDATE alarms SET incident_id = ?, updated = ?
                WHERE site_id = ? AND incident_id IS NULL
            """, (incident_id, now, site_id))

            conn.commit()

            # Scan for idle robots for tickets
            idle = cur.execute("""
                SELECT * FROM robots WHERE current_incident_id IS NULL ORDER BY RANDOM() LIMIT 1
            """).fetchone()

            # when found, assign them a ticket. 
            # TODO: Make this smarter, maybe path or proximity. right now they fly everywhere.
            if idle:
                site = cur.execute("SELECT lat, lon FROM cellsites WHERE id = ?", (site_id,)).fetchone()
                cur.execute("""
                    UPDATE robots SET current_incident_id = ?, target_lat = ?, target_lon = ?, updated = ?
                    WHERE id = ?
                """, (incident_id, site[0], site[1], now, idle[0]))
                cur.execute("""
                    UPDATE incidents SET assigned_to = ? WHERE id = ?
                """, (idle[1], incident_id))
                conn.commit()
                logger.info(f"Robot {idle[1]} dispatched to site {site_id}")


            conn.close()
            logger.info(f"Ticket created — INC #{incident_id} site={site_id} sev={severity}")
        except Exception as e:
            logger.error(f"Ticket simulator error: {e}", exc_info=True)
        finally:
            conn.close()
        await asyncio.sleep(interval)

async def alarm_simulator(interval=60):
    # Generates random alarms off sites
    while True:
        conn = db.connect_to_db()
        try:
            logger.debug("Simulating alarms...")
            cur = conn.cursor()
            now = int(datetime.now(timezone.utc).timestamp())
            site_id = random.choice(SITE_IDS)
            severity = random.randint(1, 5)
            text = random.choice(ALARM_TEXTS)
            status = "active"
            created_by = "simulator"
            cur.execute("""
                INSERT INTO alarms (text, severity, site_id, created, updated, status, created_by)
                VALUES (?, ?, ?, ?, ?, ?, ?)
            """, (text, severity, site_id, now, now, status, created_by))
            conn.commit()
            conn.close()
            logger.debug(f"Alarm created — site={site_id} sev={severity}")
        except Exception as e:
            logger.error(f"Alarm simulator error: {e}", exc_info=True)
        finally:
            conn.close()
        await asyncio.sleep(interval)

async def cleanup_incidents(interval=300, max_age_seconds=3600):
    global next_incident_cleanup_at
    while True:
        next_incident_cleanup_at = time.time() + interval
        await asyncio.sleep(interval)
        conn = db.connect_to_db()
        try:
            cur = conn.cursor()
            cutoff = int(datetime.now(timezone.utc).timestamp()) - max_age_seconds
            cur.execute("DELETE FROM incidents WHERE created_by = 'simulator' AND created < ?", (cutoff,))
            # NOTE: robots need to be reset or else will have deleted tickets
            cur.execute("""
                UPDATE robots SET
                    current_incident_id = NULL,
                    current_site_id = NULL,
                    target_lat = base_lat,
                    target_lon = base_lon
                WHERE current_incident_id IS NOT NULL
                AND current_incident_id NOT IN (SELECT id FROM incidents)
            """)
            affected = cur.rowcount
            if affected:
                logger.info(f"Reset {affected} robots orphaned by incident cleanup")
            conn.commit()
            conn.close()
            logger.info(f"Incident cleanup done — removed records older than {max_age_seconds}s")
        except Exception as e:
            logger.error(f"Incident cleanup error: {e}", exc_info=True)
        finally:
            conn.close()

async def cleanup_alarms(interval=300, max_age_seconds=3600):
    global next_alarm_cleanup_at
    while True:
        next_alarm_cleanup_at = time.time() + interval
        await asyncio.sleep(interval)
        conn = db.connect_to_db()
        try:
            cur = conn.cursor()
            cutoff = int(datetime.now(timezone.utc).timestamp()) - max_age_seconds
            cur.execute("DELETE FROM alarms WHERE created_by = 'simulator' AND created < ?", (cutoff,))

            # NOTE: Here we clear any oprhaned incident_ids off the alarm after deleted.
            cur.execute("""
                UPDATE alarms SET incident_id = NULL
                WHERE incident_id IS NOT NULL
                AND incident_id NOT IN (SELECT id FROM incidents)
            """)

            conn.commit()
            conn.close()
            logger.info(f"Alarm cleanup done — removed records older than {max_age_seconds}s")
        except Exception as e:
            logger.error(f"Alarm cleanup error: {e}", exc_info=True)
        finally:
            conn.close()

async def robot_simulator(interval=3):
    """
    This is the robot sim that will fly around and "fix" sites after being assigned tickets.
    The robot has a fixed time to "work" and then will close the ticket and move on.
    """
    while True:
        conn = db.connect_to_db()
        try:
            conn.row_factory = sqlite3.Row
            cur = conn.cursor()
            now = int(time.time())

            in_transit = cur.execute("""
                SELECT r.id, r.lat, r.lon, r.target_lat, r.target_lon,
                    r.current_incident_id, i.site_id as target_site_id
                FROM robots r
                JOIN incidents i ON r.current_incident_id = i.id
                WHERE r.current_incident_id IS NOT NULL
                AND r.current_site_id IS NULL
                AND r.target_lat IS NOT NULL
            """).fetchall()

            STEP = 0.2
            THRESHOLD = 0.05

            for robot in in_transit:
                dlat = robot['target_lat'] - robot['lat']
                dlon = robot['target_lon'] - robot['lon']
                dist = (dlat**2 + dlon**2) ** 0.5

                if dist < THRESHOLD:
                    cur.execute("""
                        UPDATE robots SET lat = ?, lon = ?, current_site_id = ?, updated = ?
                        WHERE id = ?
                    """, (robot['target_lat'], robot['target_lon'], robot['target_site_id'], now, robot['id']))
                    logger.info(f"Robot {robot['id']} arrived at site {robot['target_site_id']}")
                else:
                    factor = min(STEP / dist, 1.0)
                    new_lat = robot['lat'] + dlat * factor
                    new_lon = robot['lon'] + dlon * factor
                    cur.execute("""
                        UPDATE robots SET lat = ?, lon = ?, updated = ? WHERE id = ?
                    """, (new_lat, new_lon, now, robot['id']))

            WORK_DURATION = 60  # seconds on site before closing ticket

            on_site = cur.execute("""
                SELECT id, current_incident_id, current_site_id, base_lat, base_lon, updated
                FROM robots
                WHERE current_site_id IS NOT NULL
                AND current_incident_id IS NOT NULL
            """).fetchall()


            for robot in on_site:
                time_on_site = now - robot['updated']
                if time_on_site < WORK_DURATION:
                    continue

                # Close the incident <- not delete
                cur.execute("""
                    UPDATE incidents SET status = 'closed', updated = ? WHERE id = ?
                """, (now, robot['current_incident_id']))

                # End assignment and return, for more work
                cur.execute("""
                    UPDATE robots SET
                        current_incident_id = NULL,
                        current_site_id = NULL,
                        target_lat = base_lat,
                        target_lon = base_lon,
                        updated = ?
                    WHERE id = ?
                """, (now, robot['id']))
                logger.info(f"Robot {robot['id']} finished INC #{robot['current_incident_id']} at site {robot['current_site_id']} — returning to base")

            conn.commit()
        except Exception as e:
            logger.error(f"Robot simulator error: {e}", exc_info=True)
        finally:
            conn.close()
        await asyncio.sleep(interval)
