"""
Blood Pressure Router - v9d Phase 2d Refactored

Context-dependent blood pressure measurements (multiple times per day):
- Systolic/Diastolic Blood Pressure
- Pulse during measurement
- Context tagging (morning_fasted, after_meal, before_training, etc.)
- Warning flags (irregular heartbeat, AFib)

Endpoints:
- GET    /api/blood-pressure              List BP measurements
- GET    /api/blood-pressure/by-date/{date}  Get measurements for specific date
- POST   /api/blood-pressure              Create BP measurement
- PUT    /api/blood-pressure/{id}         Update BP measurement
- DELETE /api/blood-pressure/{id}         Delete BP measurement
- GET    /api/blood-pressure/stats        Statistics and trends
- POST   /api/blood-pressure/import/omron Import Omron CSV
"""
from fastapi import APIRouter, HTTPException, Depends, Header, UploadFile, File
from pydantic import BaseModel
from typing import Optional
from datetime import datetime, timedelta
import logging
import csv
import io

from db import get_db, get_cursor, r2d
from auth import require_auth
from routers.profiles import get_pid

router = APIRouter(prefix="/api/blood-pressure", tags=["blood_pressure"])
logger = logging.getLogger(__name__)


# German month mapping for Omron dates
GERMAN_MONTHS = {
    'Januar': '01', 'Jan.': '01', 'Jan': '01',
    'Februar': '02', 'Feb.': '02', 'Feb': '02',
    'März': '03', 'Mär.': '03', 'Mär': '03',
    'April': '04', 'Apr.': '04', 'Apr': '04',
    'Mai': '05',
    'Juni': '06', 'Jun.': '06', 'Jun': '06',
    'Juli': '07', 'Jul.': '07', 'Jul': '07',
    'August': '08', 'Aug.': '08', 'Aug': '08',
    'September': '09', 'Sep.': '09', 'Sep': '09',
    'Oktober': '10', 'Okt.': '10', 'Okt': '10',
    'November': '11', 'Nov.': '11', 'Nov': '11',
    'Dezember': '12', 'Dez.': '12', 'Dez': '12',
}


# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Pydantic Models
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

class BPEntry(BaseModel):
    measured_at: str  # ISO format datetime
    systolic: int
    diastolic: int
    pulse: Optional[int] = None
    context: Optional[str] = None  # morning_fasted, after_meal, etc.
    irregular_heartbeat: Optional[bool] = False
    possible_afib: Optional[bool] = False
    note: Optional[str] = None


# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Helper Functions
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

def parse_omron_date(date_str: str, time_str: str) -> str:
    """
    Parse Omron German date/time format to ISO datetime.
    Input: "13 März 2026", "08:30"
    Output: "2026-03-13 08:30:00"
    """
    try:
        parts = date_str.strip().split()
        if len(parts) != 3:
            return None

        day = parts[0]
        month_name = parts[1]
        year = parts[2]

        month = GERMAN_MONTHS.get(month_name)
        if not month:
            return None

        iso_date = f"{year}-{month}-{day.zfill(2)}"
        iso_datetime = f"{iso_date} {time_str}:00"

        # Validate
        datetime.fromisoformat(iso_datetime)
        return iso_datetime

    except Exception as e:
        logger.error(f"Error parsing Omron date: {date_str} {time_str} - {e}")
        return None


# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# CRUD Endpoints
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

@router.get("")
def list_bp_measurements(
    limit: int = 90,
    x_profile_id: Optional[str] = Header(default=None),
    session: dict = Depends(require_auth)
):
    """Get blood pressure measurements (last N entries)."""
    pid = get_pid(x_profile_id)
    with get_db() as conn:
        cur = get_cursor(conn)
        cur.execute("""
            SELECT * FROM blood_pressure_log
            WHERE profile_id = %s
            ORDER BY measured_at DESC
            LIMIT %s
        """, (pid, limit))
        return [r2d(r) for r in cur.fetchall()]


@router.get("/by-date/{date}")
def get_bp_by_date(
    date: str,
    x_profile_id: Optional[str] = Header(default=None),
    session: dict = Depends(require_auth)
):
    """Get all BP measurements for a specific date."""
    pid = get_pid(x_profile_id)
    with get_db() as conn:
        cur = get_cursor(conn)
        cur.execute("""
            SELECT * FROM blood_pressure_log
            WHERE profile_id = %s
              AND DATE(measured_at) = %s
            ORDER BY measured_at ASC
        """, (pid, date))
        return [r2d(r) for r in cur.fetchall()]


@router.post("")
def create_bp_measurement(
    entry: BPEntry,
    x_profile_id: Optional[str] = Header(default=None),
    session: dict = Depends(require_auth)
):
    """Create new BP measurement."""
    pid = get_pid(x_profile_id)

    with get_db() as conn:
        cur = get_cursor(conn)
        cur.execute("""
            INSERT INTO blood_pressure_log (
                profile_id, measured_at,
                systolic, diastolic, pulse,
                context, irregular_heartbeat, possible_afib,
                note, source
            ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, 'manual')
            RETURNING *
        """, (
            pid, entry.measured_at,
            entry.systolic, entry.diastolic, entry.pulse,
            entry.context, entry.irregular_heartbeat, entry.possible_afib,
            entry.note
        ))
        return r2d(cur.fetchone())


@router.put("/{entry_id}")
def update_bp_measurement(
    entry_id: int,
    entry: BPEntry,
    x_profile_id: Optional[str] = Header(default=None),
    session: dict = Depends(require_auth)
):
    """Update existing BP measurement."""
    pid = get_pid(x_profile_id)

    with get_db() as conn:
        cur = get_cursor(conn)
        cur.execute("""
            UPDATE blood_pressure_log
            SET measured_at = %s,
                systolic = %s,
                diastolic = %s,
                pulse = %s,
                context = %s,
                irregular_heartbeat = %s,
                possible_afib = %s,
                note = %s
            WHERE id = %s AND profile_id = %s
            RETURNING *
        """, (
            entry.measured_at,
            entry.systolic, entry.diastolic, entry.pulse,
            entry.context, entry.irregular_heartbeat, entry.possible_afib,
            entry.note,
            entry_id, pid
        ))
        row = cur.fetchone()
        if not row:
            raise HTTPException(404, "Entry not found")
        return r2d(row)


@router.delete("/{entry_id}")
def delete_bp_measurement(
    entry_id: int,
    x_profile_id: Optional[str] = Header(default=None),
    session: dict = Depends(require_auth)
):
    """Delete BP measurement."""
    pid = get_pid(x_profile_id)
    with get_db() as conn:
        cur = get_cursor(conn)
        cur.execute("""
            DELETE FROM blood_pressure_log
            WHERE id = %s AND profile_id = %s
        """, (entry_id, pid))
        if cur.rowcount == 0:
            raise HTTPException(404, "Entry not found")
        return {"ok": True}


# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Statistics & Trends
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

@router.get("/stats")
def get_bp_stats(
    days: int = 30,
    x_profile_id: Optional[str] = Header(default=None),
    session: dict = Depends(require_auth)
):
    """Get blood pressure statistics and trends."""
    pid = get_pid(x_profile_id)
    cutoff_date = datetime.now() - timedelta(days=days)

    with get_db() as conn:
        cur = get_cursor(conn)
        cur.execute("""
            SELECT
                COUNT(*) as total_measurements,
                -- Overall averages
                AVG(systolic) as avg_systolic,
                AVG(diastolic) as avg_diastolic,
                AVG(pulse) FILTER (WHERE pulse IS NOT NULL) as avg_pulse,
                -- 7-day averages
                AVG(systolic) FILTER (WHERE measured_at >= NOW() - INTERVAL '7 days') as avg_systolic_7d,
                AVG(diastolic) FILTER (WHERE measured_at >= NOW() - INTERVAL '7 days') as avg_diastolic_7d,
                -- Context-specific averages
                AVG(systolic) FILTER (WHERE context = 'morning_fasted') as avg_systolic_morning,
                AVG(diastolic) FILTER (WHERE context = 'morning_fasted') as avg_diastolic_morning,
                AVG(systolic) FILTER (WHERE context = 'evening') as avg_systolic_evening,
                AVG(diastolic) FILTER (WHERE context = 'evening') as avg_diastolic_evening,
                -- Warning flags
                COUNT(*) FILTER (WHERE irregular_heartbeat = true) as irregular_count,
                COUNT(*) FILTER (WHERE possible_afib = true) as afib_count
            FROM blood_pressure_log
            WHERE profile_id = %s AND measured_at >= %s
        """, (pid, cutoff_date))

        stats = r2d(cur.fetchone())

        # Classify BP ranges (WHO/ISH guidelines)
        if stats['avg_systolic'] and stats['avg_diastolic']:
            if stats['avg_systolic'] < 120 and stats['avg_diastolic'] < 80:
                stats['bp_category'] = 'optimal'
            elif stats['avg_systolic'] < 130 and stats['avg_diastolic'] < 85:
                stats['bp_category'] = 'normal'
            elif stats['avg_systolic'] < 140 and stats['avg_diastolic'] < 90:
                stats['bp_category'] = 'high_normal'
            elif stats['avg_systolic'] < 160 and stats['avg_diastolic'] < 100:
                stats['bp_category'] = 'grade_1_hypertension'
            elif stats['avg_systolic'] < 180 and stats['avg_diastolic'] < 110:
                stats['bp_category'] = 'grade_2_hypertension'
            else:
                stats['bp_category'] = 'grade_3_hypertension'
        else:
            stats['bp_category'] = None

        return stats


# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
# Import: Omron CSV
# ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

@router.post("/import/omron")
async def import_omron_csv(
    file: UploadFile = File(...),
    x_profile_id: Optional[str] = Header(default=None),
    session: dict = Depends(require_auth)
):
    """Import blood pressure measurements from Omron CSV export."""
    pid = get_pid(x_profile_id)

    content = await file.read()
    decoded = content.decode('utf-8')
    reader = csv.DictReader(io.StringIO(decoded))

    inserted = 0
    updated = 0
    skipped = 0
    errors = 0

    with get_db() as conn:
        cur = get_cursor(conn)

        # Log available columns for debugging
        first_row = True

        for row in reader:
            try:
                if first_row:
                    logger.info(f"Omron CSV Columns: {list(row.keys())}")
                    first_row = False

                # Parse Omron German date format
                date_str = row.get('Datum', row.get('Date'))
                time_str = row.get('Zeit', row.get('Time', '08:00'))

                if not date_str:
                    skipped += 1
                    continue

                measured_at = parse_omron_date(date_str, time_str)
                if not measured_at:
                    errors += 1
                    continue

                # Extract measurements (support column names with/without units)
                systolic = (row.get('Systolisch (mmHg)') or row.get('Systolisch') or
                           row.get('Systolic (mmHg)') or row.get('Systolic'))
                diastolic = (row.get('Diastolisch (mmHg)') or row.get('Diastolisch') or
                            row.get('Diastolic (mmHg)') or row.get('Diastolic'))
                pulse = (row.get('Puls (bpm)') or row.get('Puls') or
                        row.get('Pulse (bpm)') or row.get('Pulse'))

                if not systolic or not diastolic:
                    logger.warning(f"Skipped row {date_str} {time_str}: Missing BP values (sys={systolic}, dia={diastolic})")
                    skipped += 1
                    continue

                # Parse warning flags (support various column names)
                irregular = (row.get('Unregelmäßiger Herzschlag festgestellt') or
                            row.get('Unregelmäßiger Herzschlag') or
                            row.get('Irregular Heartbeat') or '')
                afib = (row.get('Mögliches AFib') or
                       row.get('Vorhofflimmern') or
                       row.get('Possible AFib') or
                       row.get('AFib') or '')

                irregular_heartbeat = irregular.lower() in ['ja', 'yes', 'true', '1']
                possible_afib = afib.lower() in ['ja', 'yes', 'true', '1']

                # Determine context based on time
                hour = int(time_str.split(':')[0])
                if 5 <= hour < 10:
                    context = 'morning_fasted'
                elif 18 <= hour < 23:
                    context = 'evening'
                else:
                    context = 'other'

                # Upsert
                cur.execute("""
                    INSERT INTO blood_pressure_log (
                        profile_id, measured_at,
                        systolic, diastolic, pulse,
                        context, irregular_heartbeat, possible_afib,
                        source
                    ) VALUES (%s, %s, %s, %s, %s, %s, %s, %s, 'omron')
                    ON CONFLICT (profile_id, measured_at)
                    DO UPDATE SET
                        systolic = EXCLUDED.systolic,
                        diastolic = EXCLUDED.diastolic,
                        pulse = EXCLUDED.pulse,
                        context = EXCLUDED.context,
                        irregular_heartbeat = EXCLUDED.irregular_heartbeat,
                        possible_afib = EXCLUDED.possible_afib
                    WHERE blood_pressure_log.source != 'manual'
                    RETURNING (xmax = 0) AS inserted
                """, (
                    pid, measured_at,
                    int(systolic), int(diastolic),
                    int(pulse) if pulse else None,
                    context, irregular_heartbeat, possible_afib
                ))

                result = cur.fetchone()
                if result is None:
                    # WHERE clause prevented update (manual entry exists)
                    skipped += 1
                elif result['inserted']:
                    inserted += 1
                else:
                    updated += 1

            except Exception as e:
                logger.error(f"Error importing Omron row: {e}")
                errors += 1

    return {
        "inserted": inserted,
        "updated": updated,
        "skipped": skipped,
        "errors": errors
    }
