import hashlib
from pathlib import Path
from typing import Any

from fastapi import APIRouter, File, Form, HTTPException, Request, UploadFile
from fastapi.responses import RedirectResponse

from app.config import settings
from app.services.bundles import SubmissionBundleService
from app.services.d2l import D2LService
from app.services.paperless import PaperlessService
from app.services.repository import get_repository
from app.services.store import JsonStore
from app.services.webhooks import WebhookService


router = APIRouter(prefix="/api/assignments", tags=["assignments"])
store = JsonStore()
repo = get_repository()
d2l = D2LService()
paperless = PaperlessService()
webhooks = WebhookService()
bundles = SubmissionBundleService()


def _confirm_submission_to_paperless(current: dict) -> dict:
    handoff = paperless.assignment_intake_payload(current)
    file_path = current.get("stored_path") or current.get("local_file")
    if not file_path or not Path(file_path).exists():
        raise HTTPException(status_code=400, detail="Stored upload file is missing.")
    fields = {
        "class_name": handoff["class_name"],
        "assignment_name": handoff["assignment_name"],
        "submission_kind": handoff["submission_kind"],
        "notes": handoff.get("notes", ""),
        "source": handoff.get("source", "doris-schoolhouse"),
    }
    for optional_key in ("semester", "course_code", "paper_kind", "telegram_chat_id", "telegram_message_id"):
        value = handoff.get(optional_key)
        if value:
            fields[optional_key] = value
    artifact_kind = handoff.get("artifact_kind") or "document"
    if artifact_kind == "source_bundle":
        paperless_response = paperless.upload_source_bundle_receipt(current)
        enrichment = paperless.enrich_direct_submission(current, paperless_response)
        response = {
            "ok": True,
            "status_code": 200,
            "body": paperless_response,
            "mode": "direct-paperless",
            "enrichment": enrichment,
        }
        task_id = paperless_response.get("task_id") or paperless_response.get("id")
        body_obj = {
            "paperless_document_id": enrichment.get("document_id"),
            "paperless_title": enrichment.get("title") or paperless.build_submission_title(current),
            "paperless_task_id": task_id,
            "paperless_metadata": enrichment.get("metadata_payload"),
        }
        ok = True
    else:
        if handoff.get("artifact_kind"):
            fields["artifact_kind"] = str(handoff["artifact_kind"])
        if handoff.get("file_count"):
            fields["file_count"] = str(handoff["file_count"])
        response = webhooks.post_multipart(handoff["target_url"], fields, "document", file_path)
        body = response.get("body", {}) if isinstance(response, dict) else {}
        body_obj = body[0] if isinstance(body, list) and body and isinstance(body[0], dict) else (body if isinstance(body, dict) else {})
        ok = bool(response.get("ok", False)) if isinstance(response, dict) else False
    updates = {
        "status": "paperless-queued" if ok else "paperless-error",
        "confirmed_at": store.now(),
        "webhook_response": response,
        "paperless_document_id": body_obj.get("paperless_document_id") or body_obj.get("document_id"),
        "paperless_title": body_obj.get("paperless_title") or body_obj.get("title") or handoff.get("assignment_name"),
        "paperless_task_id": body_obj.get("paperless_task_id"),
    }
    updated = repo.update_submission(str(current.get("id")), updates) or current
    grade_note = None
    if ok and updated.get("assignment_id"):
        grade_note = paperless.append_grade_note_if_available(str(updated.get("assignment_id")))
    return {
        "status": "ok" if ok else "error",
        "submission": updated,
        "submissions": [updated],
        "handoff": handoff,
        "webhook": [response],
        "grade_note": grade_note,
        "file_count": handoff.get("file_count") or 1,
    }


@router.get("")
async def list_assignments():
    courses = {item["id"]: item for item in d2l.get_courses()}
    items = repo.list_assignments()
    submissions = repo.list_submissions()
    submission_counts = {}
    for submission in submissions:
        submission_counts[submission.get("assignment_id")] = submission_counts.get(submission.get("assignment_id"), 0) + 1
    for item in items:
        item["submission_count"] = submission_counts.get(item.get("id"), 0)
        item["course"] = courses.get(item.get("course_id"), {})
    return {"items": items, "backend": repo.backend}




@router.get('/submissions')
async def list_submissions():
    return {"items": repo.list_submissions(), "backend": repo.backend}

@router.post("/intake")
async def intake_assignment(
    request: Request,
    files: list[UploadFile] | None = File(None),
    file: UploadFile | None = File(None),
    class_id: str = Form(...),
    class_name: str = Form(...),
    assignment_name: str = Form(...),
    version: str = Form("v1"),
    submission_kind: str = Form("assignment_turn_in"),
    semester: str = Form(""),
    course_code: str = Form(""),
    paper_kind: str = Form(""),
    source: str = Form("doris-schoolhouse"),
    telegram_chat_id: str = Form(""),
    telegram_message_id: str = Form(""),
    notes: str = Form(""),
):
    assignments = d2l.get_assignments(class_id)
    matching = next((item for item in assignments if item.get("title") == assignment_name), None)
    if not matching:
        raise HTTPException(status_code=404, detail="Assignment not found in cached D2L data for selected class.")
    uploads = list(files or [])
    if file is not None:
        uploads.append(file)
    if not uploads:
        raise HTTPException(status_code=400, detail="At least one upload is required.")

    upload_root = Path(settings.upload_tmp_dir)
    upload_root.mkdir(parents=True, exist_ok=True)
    course = next((item for item in d2l.get_courses() if str(item.get("d2l_course_id") or item.get("id")) == str(class_id)), None)
    duplicate_names: dict[str, int] = {}
    prepared_uploads: list[dict[str, Any]] = []

    for index, upload in enumerate(uploads, start=1):
        safe_name = Path(upload.filename or f"upload-{index}.bin").name
        duplicate_count = duplicate_names.get(safe_name, 0)
        duplicate_names[safe_name] = duplicate_count + 1
        if duplicate_count:
            stem = Path(safe_name).stem
            suffix = Path(safe_name).suffix
            safe_name = f"{stem}-{duplicate_count + 1}{suffix}"
        payload = await upload.read()
        prepared_uploads.append({
            "filename": safe_name,
            "payload": payload,
            "mime_type": upload.content_type or "application/octet-stream",
        })

    submission_id = store.new_id("submission")
    bundle = bundles.build_bundle(
        upload_root=upload_root,
        submission_id=submission_id,
        class_name=class_name,
        assignment_name=assignment_name,
        version_label=version,
        uploads=prepared_uploads,
        notes=notes,
    )
    record = {
        "id": submission_id,
        "assignment_id": matching["id"],
        "class_id": class_id,
        "class_name": class_name,
        "assignment_name": assignment_name,
        "version_label": version,
        "submission_kind": submission_kind,
        "artifact_kind": bundle["artifact_kind"],
        "semester": semester or None,
        "course_code": course_code or (course.get("code") if course else None),
        "paper_kind": paper_kind or None,
        "source": source or "doris-schoolhouse",
        "telegram_chat_id": telegram_chat_id or None,
        "telegram_message_id": telegram_message_id or None,
        "notes": notes,
        "original_filename": bundle["bundle_filename"],
        "stored_path": bundle["bundle_path"],
        "stored_paths": [item["stored_path"] for item in bundle["file_manifest_json"]],
        "bundle_path": bundle["bundle_path"],
        "bundle_filename": bundle["bundle_filename"],
        "source_listing_path": bundle["source_listing_path"],
        "receipt_pdf_path": bundle["receipt_pdf_path"],
        "file_manifest_json": bundle["file_manifest_json"],
        "mime_type": bundle["bundle_mime_type"],
        "file_size_bytes": bundle["bundle_size_bytes"],
        "checksum_sha256": bundle["bundle_checksum_sha256"],
        "status": "uploaded-local",
        "received_at": store.now(),
        "batch_id": None,
        "file_count": bundle["file_count"],
        "file_index": None,
        "combined_source_checksum_sha256": bundle["combined_source_checksum_sha256"],
    }
    record["paperless_handoff"] = paperless.assignment_intake_payload(record)
    created = repo.create_submission(record)
    accept = request.headers.get("accept", "")
    auto_confirmed = None
    if "text/html" in accept:
        auto_confirmed = _confirm_submission_to_paperless(created)
        created = auto_confirmed["submission"]
    response_payload = {
        "status": "ok",
        "submission": created,
        "submissions": [created],
        "batch_id": None,
        "file_count": bundle["file_count"],
        "paperless": auto_confirmed,
        "backend": repo.backend,
    }
    if "text/html" in accept:
        redirect_url = "/assignments/upload?submitted=" + str(created["id"]) + "&file_count=" + str(bundle["file_count"]) + "&artifact_kind=" + str(created.get("artifact_kind", "document")) + "&paperless_status=" + str(created.get("status", "uploaded-local"))
        return RedirectResponse(url=redirect_url, status_code=303)
    return response_payload


@router.post("/{submission_id}/confirm-paperless")
async def confirm_assignment_paperless(submission_id: str):
    submissions = repo.list_submissions()
    current = next((item for item in submissions if str(item.get("id")) == str(submission_id)), None)
    if not current:
        raise HTTPException(status_code=404, detail="Submission not found.")
    return _confirm_submission_to_paperless(current)


@router.post("/{assignment_id}/status")
async def update_assignment_status(assignment_id: str, status: str = Form(...)):
    assignments = repo.list_assignments()
    updated = None
    for item in assignments:
        if str(item.get("id")) == str(assignment_id):
            item["status"] = status
            item["updated_at"] = store.now()
            updated = item
            break
    if not updated:
        raise HTTPException(status_code=404, detail="Assignment not found.")
    repo.replace_assignments(assignments)
    return {"status": "ok", "assignment": updated}
