"""
CallKeep — Dein Beziehungs-Tracker
FastAPI Backend + SQLite + Static Frontend
"""
import os
import sqlite3
from datetime import datetime, date, timedelta
from pathlib import Path
from contextlib import contextmanager
from fastapi import FastAPI, HTTPException, Query
from fastapi.responses import FileResponse
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import Optional

FRONTEND_DIR = Path(__file__).parent / "frontend"
DATA_DIR = Path(os.environ.get("DATA_DIR", Path(__file__).parent))
DB_PATH = DATA_DIR / "callkeep.db"

from fastapi.staticfiles import StaticFiles
from fastapi import Request

app = FastAPI(title="CallKeep")
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])

@app.middleware("http")
async def allow_iframe(request: Request, call_next):
    response = await call_next(request)
    response.headers["X-Frame-Options"] = "ALLOWALL"
    response.headers["Content-Security-Policy"] = "frame-ancestors 'self' http://*:* https://*:*;"
    return response

# ── Database ──────────────────────────────────────────────────────
@contextmanager
def get_db():
    conn = sqlite3.connect(str(DB_PATH))
    conn.row_factory = sqlite3.Row
    conn.execute("PRAGMA journal_mode=WAL")
    conn.execute("PRAGMA foreign_keys=ON")
    try:
        yield conn
    finally:
        conn.close()

def init_db():
    with get_db() as db:
        db.execute("""
            CREATE TABLE IF NOT EXISTS contacts (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                name TEXT NOT NULL,
                relationship TEXT NOT NULL DEFAULT 'Freund',
                frequency TEXT NOT NULL DEFAULT 'Wöchentlich',
                priority INTEGER DEFAULT 3,
                notes TEXT DEFAULT '',
                birthday TEXT DEFAULT '',
                photo_url TEXT DEFAULT '',
                created_at TEXT DEFAULT (datetime('now')),
                updated_at TEXT DEFAULT (datetime('now'))
            )
        """)
        db.execute("""
            CREATE TABLE IF NOT EXISTS calls (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                contact_id INTEGER NOT NULL,
                direction TEXT NOT NULL DEFAULT 'Ausgehend',
                duration_minutes INTEGER DEFAULT 0,
                topics TEXT DEFAULT '',
                mood TEXT DEFAULT 'neutral',
                summary TEXT DEFAULT '',
                follow_up_needed INTEGER DEFAULT 0,
                follow_up_note TEXT DEFAULT '',
                called_at TEXT NOT NULL,
                next_call_at TEXT DEFAULT '',
                created_at TEXT DEFAULT (datetime('now')),
                FOREIGN KEY (contact_id) REFERENCES contacts(id) ON DELETE CASCADE
            )
        """)
        # Migrations for existing DBs
        existing_cols = {r[1] for r in db.execute("PRAGMA table_info(calls)").fetchall()}
        if "summary" not in existing_cols:
            db.execute("ALTER TABLE calls ADD COLUMN summary TEXT DEFAULT ''")
        db.commit()

init_db()

# ── Models ────────────────────────────────────────────────────────
class ContactCreate(BaseModel):
    name: str
    frequency: str = "Wöchentlich"
    notes: str = ""
    photo_url: str = ""

class CallCreate(BaseModel):
    contact_id: int
    direction: str = "Ausgehend"
    duration_minutes: int = 0
    summary: str = ""
    follow_up_needed: bool = False
    follow_up_note: str = ""
    called_at: str
    next_call_at: str = ""

# ── Helpers ───────────────────────────────────────────────────────
def contact_row(c):
    return {
        "id": c["id"], "name": c["name"],
        "frequency": c["frequency"],
        "notes": c["notes"],
        "photo_url": c["photo_url"],
        "created_at": c["created_at"], "updated_at": c["updated_at"]
    }

def call_row(c):
    return {
        "id": c["id"], "contact_id": c["contact_id"],
        "direction": c["direction"], "duration_minutes": c["duration_minutes"],
        "summary": c["summary"] if "summary" in c.keys() else "",
        "follow_up_needed": bool(c["follow_up_needed"]),
        "follow_up_note": c["follow_up_note"],
        "called_at": c["called_at"], "next_call_at": c["next_call_at"],
        "created_at": c["created_at"]
    }

FREQ_DAYS = {
    "Täglich": 1,
    "Wöchentlich": 7,
    "Alle 2 Wochen": 14,
    "Dreiwöchentlich": 21,
    "Monatlich": 30,
    "Alle 6 Wochen": 42,
    "Alle 2 Monate": 60,
    "Vierteljährlich": 90,
}

def days_until_next(contact: dict, last_call: Optional[dict]) -> int:
    days = FREQ_DAYS.get(contact.get("frequency", "Wöchentlich"), 7)
    if last_call and last_call.get("next_call_at"):
        try:
            target = datetime.fromisoformat(last_call["next_call_at"]).date()
            return (target - date.today()).days
        except Exception:
            pass
    if last_call:
        try:
            call_date = datetime.fromisoformat(last_call["called_at"]).date()
            return days - (date.today() - call_date).days
        except Exception:
            pass
    return -999

# ── Frontend ──────────────────────────────────────────────────────
@app.get("/")
def serve_frontend():
    return FileResponse(FRONTEND_DIR / "index.html")

@app.get("/icon.svg")
def serve_icon():
    return FileResponse(FRONTEND_DIR / "icon.svg", media_type="image/svg+xml")

# ── Contacts API ──────────────────────────────────────────────────
@app.get("/api/contacts")
def list_contacts():
    with get_db() as db:
        rows = db.execute("SELECT * FROM contacts ORDER BY name").fetchall()
        return [contact_row(r) for r in rows]

@app.post("/api/contacts")
def create_contact(c: ContactCreate):
    with get_db() as db:
        cur = db.execute(
            "INSERT INTO contacts (name, frequency, notes, photo_url) VALUES (?,?,?,?)",
            [c.name, c.frequency, c.notes, c.photo_url]
        )
        db.commit()
        new = db.execute("SELECT * FROM contacts WHERE id=?", (cur.lastrowid,)).fetchone()
        return contact_row(new)

@app.put("/api/contacts/{contact_id}")
def update_contact(contact_id: int, c: ContactCreate):
    with get_db() as db:
        db.execute(
            "UPDATE contacts SET name=?, frequency=?, notes=?, photo_url=?, updated_at=datetime('now') WHERE id=?",
            [c.name, c.frequency, c.notes, c.photo_url, contact_id]
        )
        db.commit()
        row = db.execute("SELECT * FROM contacts WHERE id=?", (contact_id,)).fetchone()
        if not row:
            raise HTTPException(404, "Kontakt nicht gefunden")
        return contact_row(row)

@app.delete("/api/contacts/{contact_id}")
def delete_contact(contact_id: int):
    with get_db() as db:
        db.execute("DELETE FROM contacts WHERE id=?", (contact_id,))
        db.commit()
        return {"ok": True}

# ── Calls API ─────────────────────────────────────────────────────
@app.get("/api/contacts/{contact_id}/calls")
def list_calls(contact_id: int, limit: int = 50):
    with get_db() as db:
        rows = db.execute(
            "SELECT * FROM calls WHERE contact_id=? ORDER BY called_at DESC LIMIT ?",
            (contact_id, limit)
        ).fetchall()
        return [call_row(r) for r in rows]

@app.get("/api/calls")
def list_all_calls(limit: int = 50, contact_id: Optional[int] = None):
    with get_db() as db:
        if contact_id is not None:
            rows = db.execute(
                """SELECT c.*, co.name as contact_name FROM calls c
                   JOIN contacts co ON c.contact_id = co.id
                   WHERE c.contact_id = ?
                   ORDER BY c.called_at DESC LIMIT ?""",
                (contact_id, limit)
            ).fetchall()
        else:
            rows = db.execute(
                """SELECT c.*, co.name as contact_name FROM calls c
                   JOIN contacts co ON c.contact_id = co.id
                   ORDER BY c.called_at DESC LIMIT ?""",
                (limit,)
            ).fetchall()
        result = []
        for r in rows:
            item = call_row(r)
            item["contact_name"] = r["contact_name"]
            result.append(item)
        return result

@app.post("/api/calls")
def create_call(c: CallCreate):
    with get_db() as db:
        cur = db.execute(
            "INSERT INTO calls (contact_id, direction, duration_minutes, summary, follow_up_needed, follow_up_note, called_at, next_call_at) VALUES (?,?,?,?,?,?,?,?)",
            [c.contact_id, c.direction, c.duration_minutes, c.summary,
             int(c.follow_up_needed), c.follow_up_note, c.called_at, c.next_call_at]
        )
        db.commit()
        new = db.execute("SELECT * FROM calls WHERE id=?", (cur.lastrowid,)).fetchone()
        return call_row(new)

@app.put("/api/calls/{call_id}")
def update_call(call_id: int, c: CallCreate):
    with get_db() as db:
        db.execute(
            "UPDATE calls SET contact_id=?, direction=?, duration_minutes=?, summary=?, follow_up_needed=?, follow_up_note=?, called_at=?, next_call_at=? WHERE id=?",
            [c.contact_id, c.direction, c.duration_minutes, c.summary,
             int(c.follow_up_needed), c.follow_up_note, c.called_at, c.next_call_at, call_id]
        )
        db.commit()
        row = db.execute("SELECT * FROM calls WHERE id=?", (call_id,)).fetchone()
        if not row:
            raise HTTPException(404, "Anruf nicht gefunden")
        return call_row(row)

@app.delete("/api/calls/{call_id}")
def delete_call(call_id: int):
    with get_db() as db:
        db.execute("DELETE FROM calls WHERE id=?", (call_id,))
        db.commit()
        return {"ok": True}

# ── Dashboard API ─────────────────────────────────────────────────
@app.get("/api/dashboard")
def dashboard():
    with get_db() as db:
        contacts = db.execute("SELECT * FROM contacts ORDER BY name").fetchall()
        result = []
        for c in contacts:
            last = db.execute(
                "SELECT * FROM calls WHERE contact_id=? ORDER BY called_at DESC LIMIT 1",
                (c["id"],)
            ).fetchone()
            contact = contact_row(c)
            last_call = call_row(last) if last else None
            due_in = days_until_next(contact, last_call)
            result.append({
                "contact": contact,
                "last_call": last_call,
                "due_in_days": due_in,
                "status": "overdue" if due_in < 0 else ("soon" if due_in <= 3 else "ok")
            })
        result.sort(key=lambda x: (0 if x["status"] == "overdue" else 1 if x["status"] == "soon" else 2, x["due_in_days"]))
        return result

@app.get("/api/stats")
def stats():
    with get_db() as db:
        total_contacts = db.execute("SELECT COUNT(*) FROM contacts").fetchone()[0]
        week_start = (date.today() - timedelta(days=date.today().weekday())).isoformat()
        calls_this_week = db.execute(
            "SELECT COUNT(*) FROM calls WHERE called_at >= ?",
            (week_start,)
        ).fetchone()[0]
        return {
            "total_contacts": total_contacts,
            "calls_this_week": calls_this_week,
            "week_start": week_start
        }

# ── Kiwi Integration ──────────────────────────────────────────────
@app.post("/api/kiwi/log-call")
def kiwi_log_call(contact_name: str = Query(...),
                   direction: str = Query("Ausgehend"),
                   duration_minutes: int = Query(0),
                   summary: str = Query(""),
                   follow_up_note: str = Query(""),
                   called_at: Optional[str] = Query(None)):
    with get_db() as db:
        contact = db.execute("SELECT id FROM contacts WHERE name LIKE ?", (f"%{contact_name}%",)).fetchone()
        if not contact:
            cur = db.execute(
                "INSERT INTO contacts (name, frequency) VALUES (?, 'Wöchentlich')",
                (contact_name,)
            )
            db.commit()
            contact_id = cur.lastrowid
        else:
            contact_id = contact["id"]

        call_time = called_at or date.today().isoformat()
        cur = db.execute(
            "INSERT INTO calls (contact_id, direction, duration_minutes, summary, follow_up_needed, follow_up_note, called_at) VALUES (?,?,?,?,?,?,?)",
            [contact_id, direction, duration_minutes, summary,
             1 if follow_up_note else 0, follow_up_note, call_time]
        )
        db.commit()
        return {"ok": True, "call_id": cur.lastrowid, "contact_id": contact_id}

# ── Static options ────────────────────────────────────────────────
@app.get("/api/options")
def options():
    return {
        "frequencies": list(FREQ_DAYS.keys()),
        "directions": ["Ausgehend", "Eingehend"]
    }

# ── Static files ──────────────────────────────────────────────────
if FRONTEND_DIR.exists():
    app.mount("/", StaticFiles(directory=str(FRONTEND_DIR), html=True), name="frontend")

# ── RUN ───────────────────────────────────────────────────────────
if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=3002)
