import json
import logging
import os
import sqlite3
from contextlib import closing
from datetime import datetime, timezone

from fastapi import FastAPI, Request
from fastapi.responses import PlainTextResponse

DB_PATH = os.getenv("WEBHOOK_DB_PATH", "./servicem8_webhooks.db")
APP_HOST = os.getenv("WEBHOOK_HOST", "0.0.0.0")
APP_PORT = int(os.getenv("WEBHOOK_PORT", "18354"))

logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
logger = logging.getLogger("servicem8-webhook-receiver")

app = FastAPI(title="ServiceM8 Webhook Receiver", version="1.1.0")


def get_conn():
    conn = sqlite3.connect(DB_PATH)
    conn.row_factory = sqlite3.Row
    return conn


def init_db():
    with closing(get_conn()) as conn:
        conn.execute(
            """
            CREATE TABLE IF NOT EXISTS webhook_events (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                received_at TEXT NOT NULL,
                client_host TEXT,
                method TEXT NOT NULL,
                path TEXT NOT NULL,
                headers_json TEXT,
                payload_json TEXT NOT NULL
            )
            """
        )

        conn.execute(
            """
            CREATE TABLE IF NOT EXISTS webhook_objects (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                received_at TEXT NOT NULL,
                client_host TEXT,
                method TEXT NOT NULL,
                path TEXT NOT NULL,
                object_type TEXT,
                object_uuid TEXT,
                changed_fields_json TEXT,
                object_time_utc TEXT,
                resource_url TEXT,
                headers_json TEXT,
                payload_json TEXT NOT NULL
            )
            """
        )

        conn.commit()


@app.on_event("startup")
async def startup_event():
    init_db()
    logger.info("SQLite DB ready at %s", DB_PATH)


@app.get("/health")
async def health():
    return {"ok": True}


async def parse_request_payload(request: Request):
    content_type = request.headers.get("content-type", "").lower()

    if "application/json" in content_type:
        try:
            return await request.json()
        except Exception:
            body = await request.body()
            return {"_raw_body": body.decode("utf-8", errors="replace")}

    if "application/x-www-form-urlencoded" in content_type or "multipart/form-data" in content_type:
        try:
            form = await request.form()
            return dict(form)
        except Exception:
            body = await request.body()
            return {"_raw_body": body.decode("utf-8", errors="replace")}

    try:
        return await request.json()
    except Exception:
        body = await request.body()
        return {"_raw_body": body.decode("utf-8", errors="replace")}


@app.post("/webhooks/servicem8-job-updated")
async def servicem8_event_webhook(request: Request):
    payload = await parse_request_payload(request)
    headers = dict(request.headers)
    client_host = request.client.host if request.client else None
    received_at = datetime.now(timezone.utc).isoformat()

    with closing(get_conn()) as conn:
        conn.execute(
            """
            INSERT INTO webhook_events (
                received_at,
                client_host,
                method,
                path,
                headers_json,
                payload_json
            ) VALUES (?, ?, ?, ?, ?, ?)
            """,
            (
                received_at,
                client_host,
                request.method,
                request.url.path,
                json.dumps(headers),
                json.dumps(payload),
            ),
        )
        conn.commit()

    logger.info("Event webhook received from %s and stored", client_host)
    return PlainTextResponse("OK", status_code=200)


@app.post("/webhooks/servicem8-object")
async def servicem8_object_webhook(request: Request):
    payload = await parse_request_payload(request)
    headers = dict(request.headers)
    client_host = request.client.host if request.client else None
    received_at = datetime.now(timezone.utc).isoformat()

    if isinstance(payload, dict) and payload.get("mode") == "subscribe" and payload.get("challenge"):
        challenge = str(payload["challenge"])
        logger.info("ServiceM8 webhook verification received from %s", client_host)
        return PlainTextResponse(challenge, status_code=200)

    object_type = None
    object_uuid = None
    changed_fields = None
    object_time_utc = None
    resource_url = None

    if isinstance(payload, dict):
        object_type = payload.get("object")
        resource_url = payload.get("resource_url")

        entry = payload.get("entry")
        if isinstance(entry, list) and len(entry) > 0 and isinstance(entry[0], dict):
            first_entry = entry[0]
            object_uuid = first_entry.get("uuid")
            changed_fields = first_entry.get("changed_fields")
            object_time_utc = first_entry.get("time")

    with closing(get_conn()) as conn:
        conn.execute(
            """
            INSERT INTO webhook_objects (
                received_at,
                client_host,
                method,
                path,
                object_type,
                object_uuid,
                changed_fields_json,
                object_time_utc,
                resource_url,
                headers_json,
                payload_json
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            """,
            (
                received_at,
                client_host,
                request.method,
                request.url.path,
                object_type,
                object_uuid,
                json.dumps(changed_fields) if changed_fields is not None else None,
                object_time_utc,
                resource_url,
                json.dumps(headers),
                json.dumps(payload),
            ),
        )
        conn.commit()

    logger.info(
        "Object webhook received from %s: object=%s uuid=%s",
        client_host,
        object_type,
        object_uuid,
    )
    return PlainTextResponse("OK", status_code=200)


if __name__ == "__main__":
    import uvicorn

    uvicorn.run("servicem8_webhook_receiver:app", host=APP_HOST, port=APP_PORT, reload=False)
