import logging
import time
from datetime import datetime, timezone
from typing import Optional

from apscheduler.schedulers.background import BackgroundScheduler

from config import settings

logger = logging.getLogger(__name__)

_scheduler: Optional[BackgroundScheduler] = None
_last_run: dict = {
    'timestamp': None,
    'duration_sec': 0,
    'vessel_count': 0,
    'upserted': 0,
    'error': None,
}


def get_last_run() -> dict:
    return _last_run.copy()


def run_analysis_cycle():
    """5분 주기 분석 사이클 — 인메모리 캐시 기반."""
    from cache.vessel_store import vessel_store
    from db import snpdb, kcgdb
    from pipeline.orchestrator import ChineseFishingVesselPipeline
    from algorithms.location import classify_zone
    from algorithms.fishing_pattern import compute_ucaf_score, compute_ucft_score
    from algorithms.dark_vessel import is_dark_vessel
    from algorithms.spoofing import compute_spoofing_score, count_speed_jumps, compute_bd09_offset
    from algorithms.fleet import assign_fleet_roles
    from algorithms.risk import compute_vessel_risk_score
    from models.result import AnalysisResult

    start = time.time()
    _last_run['timestamp'] = datetime.now(timezone.utc).isoformat()
    _last_run['error'] = None

    try:
        # 1. 증분 로드 + stale 제거
        if vessel_store.last_bucket is None:
            logger.warning('last_bucket is None, skipping incremental fetch (initial load not complete)')
            df_new = None
        else:
            df_new = snpdb.fetch_incremental(vessel_store.last_bucket)
        if df_new is not None and len(df_new) > 0:
            vessel_store.merge_incremental(df_new)
        vessel_store.evict_stale(settings.CACHE_WINDOW_HOURS)

        # 정적정보 / 허가어선 주기적 갱신
        vessel_store.refresh_static_info()
        vessel_store.refresh_permit_registry()

        # 2. 분석 대상 선별 (SOG/COG 계산 포함)
        df_targets = vessel_store.select_analysis_targets()
        if len(df_targets) == 0:
            logger.info('no analysis targets, skipping cycle')
            _last_run['vessel_count'] = 0
            return

        # 3. 7단계 파이프라인 실행
        pipeline = ChineseFishingVesselPipeline()
        classifications, vessel_dfs = pipeline.run(df_targets)

        if not classifications:
            logger.info('no vessels classified, skipping')
            _last_run['vessel_count'] = 0
            return

        # 4. 선단 역할 분석
        cluster_map = {c['mmsi']: c['cluster_id'] for c in classifications}
        fleet_roles = assign_fleet_roles(vessel_dfs, cluster_map)

        # 5. 선박별 추가 알고리즘 → AnalysisResult 생성
        results = []
        for c in classifications:
            mmsi = c['mmsi']
            df_v = vessel_dfs.get(mmsi)
            if df_v is None or len(df_v) == 0:
                continue

            last_row = df_v.iloc[-1]
            ts = last_row.get('timestamp')

            zone_info = classify_zone(last_row['lat'], last_row['lon'])

            gear_map = {'TRAWL': 'OT', 'PURSE': 'PS', 'LONGLINE': 'GN', 'TRAP': 'TRAP'}
            gear = gear_map.get(c['vessel_type'], 'OT')
            ucaf = compute_ucaf_score(df_v, gear)
            ucft = compute_ucft_score(df_v)

            dark, gap_min = is_dark_vessel(df_v)

            spoof_score = compute_spoofing_score(df_v)
            speed_jumps = count_speed_jumps(df_v)
            bd09_offset = compute_bd09_offset(last_row['lat'], last_row['lon'])

            fleet_info = fleet_roles.get(mmsi, {})

            is_permitted = vessel_store.is_permitted(mmsi)
            risk_score, risk_level = compute_vessel_risk_score(
                mmsi, df_v, zone_info, is_permitted=is_permitted,
            )

            activity = 'UNKNOWN'
            if 'state' in df_v.columns and len(df_v) > 0:
                activity = df_v['state'].mode().iloc[0]

            results.append(AnalysisResult(
                mmsi=mmsi,
                timestamp=ts,
                vessel_type=c['vessel_type'],
                confidence=c['confidence'],
                fishing_pct=c['fishing_pct'],
                cluster_id=c['cluster_id'],
                season=c['season'],
                zone=zone_info.get('zone', 'EEZ_OR_BEYOND'),
                dist_to_baseline_nm=zone_info.get('dist_from_baseline_nm', 999.0),
                activity_state=activity,
                ucaf_score=ucaf,
                ucft_score=ucft,
                is_dark=dark,
                gap_duration_min=gap_min,
                spoofing_score=spoof_score,
                bd09_offset_m=bd09_offset,
                speed_jump_count=speed_jumps,
                cluster_size=fleet_info.get('cluster_size', 0),
                is_leader=fleet_info.get('is_leader', False),
                fleet_role=fleet_info.get('fleet_role', 'NOISE'),
                risk_score=risk_score,
                risk_level=risk_level,
                features=c.get('features', {}),
            ))

        # 6. 결과 저장
        upserted = kcgdb.upsert_results(results)
        kcgdb.cleanup_old(hours=48)

        elapsed = round(time.time() - start, 2)
        _last_run['duration_sec'] = elapsed
        _last_run['vessel_count'] = len(results)
        _last_run['upserted'] = upserted
        logger.info(
            'analysis cycle: %d vessels, %d upserted, %.2fs',
            len(results), upserted, elapsed,
        )

    except Exception as e:
        _last_run['error'] = str(e)
        logger.exception('analysis cycle failed: %s', e)


def start_scheduler():
    global _scheduler
    _scheduler = BackgroundScheduler()
    _scheduler.add_job(
        run_analysis_cycle,
        'interval',
        minutes=settings.SCHEDULER_INTERVAL_MIN,
        id='vessel_analysis',
        max_instances=1,
        replace_existing=True,
    )
    _scheduler.start()
    logger.info('scheduler started (interval=%dm)', settings.SCHEDULER_INTERVAL_MIN)


def stop_scheduler():
    global _scheduler
    if _scheduler:
        _scheduler.shutdown(wait=False)
        _scheduler = None
        logger.info('scheduler stopped')
