import base64
import json
import logging
import re
from datetime import datetime, timedelta
from pathlib import Path
from typing import Any, Dict, Optional
from uuid import uuid4

from fastapi import APIRouter, HTTPException, Request

from app.bug_reports.backend.models import BugReportPayload, BugReportResult
from app.core.config import settings
from app.core.database import execute_query, execute_query_single

logger = logging.getLogger(__name__)
router = APIRouter()

UPLOAD_BASE_PATH = Path(settings.UPLOAD_DIR).resolve()
SAG_FILE_SUBDIR = "sag_files"
(UPLOAD_BASE_PATH / SAG_FILE_SUBDIR).mkdir(parents=True, exist_ok=True)


def _table_exists(table_name: str) -> bool:
    row = execute_query_single(
        """
        SELECT 1
        FROM information_schema.tables
        WHERE table_schema = 'public'
          AND table_name = %s
        LIMIT 1
        """,
        (table_name,),
    )
    return bool(row)


def _decode_data_url(data_url: str) -> tuple[bytes, str]:
    # Expected format: data:image/png;base64,....
    match = re.match(r"^data:([\w/+.-]+);base64,(.+)$", data_url or "", flags=re.DOTALL)
    if not match:
        raise HTTPException(status_code=400, detail="Invalid base64 data URL")

    content_type = match.group(1)
    b64_data = match.group(2)
    try:
        raw = base64.b64decode(b64_data, validate=True)
    except Exception:
        raise HTTPException(status_code=400, detail="Invalid base64 encoding")

    return raw, content_type


def _store_raw_file(raw: bytes, filename: str) -> tuple[str, int]:
    safe_name = Path(filename).name
    stored_name = f"{SAG_FILE_SUBDIR}/{uuid4().hex}_{safe_name}"
    destination = UPLOAD_BASE_PATH / stored_name
    destination.parent.mkdir(parents=True, exist_ok=True)

    with destination.open("wb") as f:
        f.write(raw)

    return stored_name, len(raw)


def _create_sag_file_record(sag_id: int, filename: str, content_type: str, size_bytes: int, stored_name: str) -> None:
    execute_query(
        """
        INSERT INTO sag_files (sag_id, filename, content_type, size_bytes, stored_name)
        VALUES (%s, %s, %s, %s, %s)
        """,
        (sag_id, filename, content_type, size_bytes, stored_name),
    )


def _rate_limit(user_id: int) -> None:
    if not _table_exists("bug_report_submissions"):
        # If migration is not yet applied, fail-open to avoid blocking support workflows.
        return

    max_per_hour = max(1, int(settings.BUG_REPORT_MAX_PER_HOUR))
    row = execute_query_single(
        """
        SELECT COUNT(*)::int AS count
        FROM bug_report_submissions
        WHERE user_id = %s
          AND created_at >= %s
        """,
        (user_id, datetime.utcnow() - timedelta(hours=1)),
    )
    count = int((row or {}).get("count") or 0)
    if count >= max_per_hour:
        raise HTTPException(status_code=429, detail="Rate limit exceeded for bug reports")


def _resolve_customer_id() -> int:
    configured_id = int(settings.BUG_REPORT_DEFAULT_CUSTOMER_ID)
    configured_row = execute_query_single("SELECT id FROM customers WHERE id = %s", (configured_id,))
    if configured_row:
        return int(configured_row["id"])

    named_row = execute_query_single(
        """
        SELECT id
        FROM customers
        WHERE LOWER(name) = LOWER(%s)
        ORDER BY id ASC
        LIMIT 1
        """,
        ("BMC Networks",),
    )
    if named_row:
        return int(named_row["id"])

    fallback = execute_query_single("SELECT id FROM customers ORDER BY id ASC LIMIT 1")
    if fallback:
        return int(fallback["id"])

    raise HTTPException(status_code=400, detail="No customers available for bug report case creation")


@router.post("/bug-reports", response_model=BugReportResult)
async def create_bug_report(payload: BugReportPayload, request: Request):
    user_id = getattr(request.state, "user_id", None) or 1

    _rate_limit(int(user_id))

    title_seed = (payload.actual or "").strip().splitlines()[0][:80]
    title = f"Bug: {title_seed or 'Ukendt fejl'}"

    metadata_json = json.dumps(payload.metadata or {}, ensure_ascii=False, indent=2)
    logs_preview = (payload.logs or [])[:50]
    logs_json = json.dumps(logs_preview, ensure_ascii=False, indent=2)

    description = (
        "## Hvad gik galt\n"
        f"{payload.actual.strip()}\n\n"
        "## Hvad burde være sket\n"
        f"{payload.expected.strip()}\n\n"
        "## Metadata\n"
        f"```json\n{metadata_json}\n```\n\n"
        "## Log preview (seneste 50)\n"
        f"```json\n{logs_json}\n```\n"
    )

    customer_id = _resolve_customer_id()
    assigned_user_id: Optional[int] = settings.BUG_REPORT_AUTO_ASSIGN_USER_ID

    created = execute_query(
        """
        INSERT INTO sag_sager
            (titel, beskrivelse, template_key, status, customer_id, ansvarlig_bruger_id, created_by_user_id)
        VALUES
            (%s, %s, %s, %s, %s, %s, %s)
        RETURNING id
        """,
        (
            title,
            description,
            "bug_report",
            "åben",
            customer_id,
            assigned_user_id,
            user_id,
        ),
    )

    if not created:
        raise HTTPException(status_code=500, detail="Failed to create bug case")

    sag_id = int(created[0]["id"])

    # Attach screenshot if provided
    if payload.screenshot_base64:
        raw, content_type = _decode_data_url(payload.screenshot_base64)
        if len(raw) > settings.BUG_REPORT_MAX_SCREENSHOT_BYTES:
            raise HTTPException(status_code=400, detail="Screenshot too large")

        stored_name, size = _store_raw_file(raw, f"bugreport_{sag_id}.png")
        _create_sag_file_record(sag_id, "screenshot.png", content_type, size, stored_name)

    # Attach logs as json file
    logs_raw = json.dumps(payload.logs or [], ensure_ascii=False, indent=2).encode("utf-8")
    stored_name, size = _store_raw_file(logs_raw, f"bugreport_{sag_id}_logs.json")
    _create_sag_file_record(sag_id, "logs.json", "application/json", size, stored_name)

    # Optional extra file
    if payload.extra_file_base64 and payload.extra_file_name:
        raw, content_type = _decode_data_url(payload.extra_file_base64)
        if len(raw) > settings.BUG_REPORT_MAX_ATTACHMENT_BYTES:
            raise HTTPException(status_code=400, detail="Extra file too large")
        stored_name, size = _store_raw_file(raw, payload.extra_file_name)
        _create_sag_file_record(sag_id, payload.extra_file_name, content_type, size, stored_name)

    # Track submission for rate-limiting/audit
    if _table_exists("bug_report_submissions"):
        execute_query(
            """
            INSERT INTO bug_report_submissions (sag_id, user_id, screenshot_attached)
            VALUES (%s, %s, %s)
            """,
            (sag_id, user_id, bool(payload.screenshot_base64)),
        )

    logger.info("✅ Bug report case created: SAG-%s by user_id=%s", sag_id, user_id)

    return BugReportResult(
        success=True,
        sag_id=sag_id,
        case_url=f"/sag/{sag_id}/v3",
        message="Fejl rapporteret og sag oprettet",
    )
