import io
import json
import re
import zipfile
from datetime import datetime, timezone

from quart import Blueprint, Response, request

from scribe.auth import login_required, get_current_user_id
from scribe.models import async_session
from scribe.models.note import Note
from sqlalchemy import select

export_bp = Blueprint("export", __name__)


def _safe_filename(title: str) -> str:
    name = re.sub(r"[^\w\s-]", "", title or "untitled").strip()
    name = re.sub(r"[\s]+", "_", name) or "untitled"
    return name[:80]


def _note_frontmatter(note: Note) -> str:
    lines = ["---"]
    lines.append(f"title: {json.dumps(note.title or '')}")
    lines.append(f"type: {'task' if note.is_task else 'note'}")
    if note.tags:
        lines.append(f"tags: [{', '.join(note.tags)}]")
    if note.is_task:
        lines.append(f"status: {note.status or 'todo'}")
        lines.append(f"priority: {note.priority or 'none'}")
    if note.due_date:
        lines.append(f"due_date: {note.due_date}")
    if note.project_id:
        lines.append(f"project_id: {note.project_id}")
    lines.append(f"created_at: {note.created_at.isoformat() if note.created_at else ''}")
    lines.append(f"updated_at: {note.updated_at.isoformat() if note.updated_at else ''}")
    lines.append("---")
    return "\n".join(lines) + "\n\n"


async def _fetch_notes(uid: int):
    async with async_session() as db:
        result = await db.execute(
            select(Note)
            .where(Note.user_id == uid)
            .order_by(Note.updated_at.desc())
        )
        return result.scalars().all()


@export_bp.get("/api/export")
@login_required
async def export_data():
    uid = get_current_user_id()
    fmt = request.args.get("format", "markdown")

    notes = await _fetch_notes(uid)

    if fmt == "json":
        payload = [
            {
                "id": n.id,
                "title": n.title,
                "body": n.body,
                "is_task": n.is_task,
                "status": n.status,
                "priority": n.priority,
                "tags": n.tags or [],
                "due_date": str(n.due_date) if n.due_date else None,
                "project_id": n.project_id,
                "milestone_id": n.milestone_id,
                "parent_id": n.parent_id,
                "created_at": n.created_at.isoformat() if n.created_at else None,
                "updated_at": n.updated_at.isoformat() if n.updated_at else None,
            }
            for n in notes
        ]
        data = json.dumps(payload, indent=2, ensure_ascii=False).encode("utf-8")
        stamp = datetime.now(timezone.utc).strftime("%Y%m%d")
        return Response(
            data,
            status=200,
            headers={
                "Content-Type": "application/json; charset=utf-8",
                "Content-Disposition": f'attachment; filename="scribe-{stamp}.json"',
            },
        )

    # Markdown ZIP
    buf = io.BytesIO()
    used_names: dict[str, int] = {}

    with zipfile.ZipFile(buf, mode="w", compression=zipfile.ZIP_DEFLATED) as zf:
        index_rows = []
        for note in notes:
            base = _safe_filename(note.title)
            count = used_names.get(base, 0)
            used_names[base] = count + 1
            fname = f"{base}_{count}.md" if count else f"{base}.md"

            content = _note_frontmatter(note) + (note.body or "")
            zf.writestr(fname, content.encode("utf-8"))
            index_rows.append({"file": fname, "title": note.title, "id": note.id})

        zf.writestr("index.json", json.dumps(index_rows, indent=2, ensure_ascii=False))

    buf.seek(0)
    stamp = datetime.now(timezone.utc).strftime("%Y%m%d")
    return Response(
        buf.read(),
        status=200,
        headers={
            "Content-Type": "application/zip",
            "Content-Disposition": f'attachment; filename="scribe-{stamp}.zip"',
        },
    )
