import io, os, json, zipfile, traceback
from fastapi import APIRouter, BackgroundTasks, HTTPException
from fastapi.responses import FileResponse, StreamingResponse
import matplotlib.pyplot as plt

from ..core import EXECUTOR
from ..schemas import StartSessionRequest, SessionStatus
from ..session import Session, SESSIONS
from ..storage import FILES
from ..utils import SavefigRedirect, collect_outputs
from ..recon_app import ReconstructionApp, ReconstructionH5

router = APIRouter()

@router.post("/reconstruct", response_model=SessionStatus)
def reconstruct(req: StartSessionRequest, background: BackgroundTasks):
    if req.file_raw_id not in FILES:  raise HTTPException(400, "file_raw_id не найден")
    if req.file_json_id not in FILES: raise HTTPException(400, "file_json_id не найден")
    if req.file_order_id and req.file_order_id not in FILES: raise HTTPException(400, "file_order_id не найден")

    session = Session(
        FILES[req.file_raw_id],
        FILES[req.file_json_id],
        FILES.get(req.file_order_id) if req.file_order_id else None,
        req.sequence_name, req.digit, req.phase_shift
    )
    SESSIONS[session.session_id] = session

    def _run():
        savefig_orig = plt.savefig
        try:
            session.set(status="running", progress=0.1, message="init")

            with SavefigRedirect(session.work_dir):
                app_reco = ReconstructionApp(name=session.sequence_name, digit=session.digit, shift=session.phase_shift)
                session.set(progress=0.2, message="read + prepare")
                app_reco.start_reconstruction(
                    path_raw_data=session.file_raw,
                    path_np_data_json=session.file_json,
                    path_order_json=session.file_order if session.file_order else session.file_json
                )
                session.set(progress=0.95, message="collect results")
                session.result_files = collect_outputs(session.work_dir)
                session.set(status="done", progress=1.0, message="done")
        except Exception:
            session.error_traceback = traceback.format_exc()
            session.set(status="error", message="error")
        finally:
            plt.savefig = savefig_orig
            with open(os.path.join(session.work_dir, "status.json"), "w", encoding="utf-8") as f:
                json.dump(session.to_status().model_dump(), f, ensure_ascii=False, indent=2)

    background.add_task(EXECUTOR.submit, _run)
    return session.to_status()

@router.post("/h5/reconstruct", response_model=SessionStatus)
def reconstructFromH5(req: StartSessionRequest, background: BackgroundTasks):
    if req.file_raw_id not in FILES:  raise HTTPException(400, "file_raw_id не найден")
    if req.file_json_id not in FILES: raise HTTPException(400, "file_json_id не найден")
    if req.file_order_id and req.file_order_id not in FILES: raise HTTPException(400, "file_order_id не найден")
    
    session = Session(
        FILES[req.file_raw_id],
        FILES[req.file_json_id],
        FILES.get(req.file_order_id) if req.file_order_id else None,
        req.sequence_name, req.digit, req.phase_shift
    )
    SESSIONS[session.session_id] = session
    
    def _run():
        savefig_orig = plt.savefig
        try:
            session.set(status="running", progress=0.1, message="init")
    
            with SavefigRedirect(session.work_dir):
                app_reco = ReconstructionH5(name=session.sequence_name, digit=session.digit, shift=session.phase_shift)
                session.set(progress=0.2, message="read + prepare")
                app_reco.start_reconstruction(
                    path_raw_data=session.file_raw,
                    path_np_data_json=session.file_json,
                    path_order_json=session.file_order if session.file_order else session.file_json
                )
                session.set(progress=0.95, message="collect results")
                session.result_files = collect_outputs(session.work_dir)
                session.set(status="done", progress=1.0, message="done")
        except Exception:
            session.error_traceback = traceback.format_exc()
            session.set(status="error", message="error")
        finally:
            plt.savefig = savefig_orig
            with open(os.path.join(session.work_dir, "status.json"), "w", encoding="utf-8") as f:
                json.dump(session.to_status().model_dump(), f, ensure_ascii=False, indent=2)
    
    background.add_task(EXECUTOR.submit, _run)
    return session.to_status()    

@router.get("/sessions/{session_id}", response_model=SessionStatus)
def get_status(session_id: str):
    session = SESSIONS.get(session_id)
    if not session: raise HTTPException(404, "не найден")
    return session.to_status()

@router.get("/sessions/{session_id}/files")
def list_files(session_id: str):
    session = SESSIONS.get(session_id)
    if not session: raise HTTPException(404, "не найден")
    files = [os.path.basename(p) for p in session.result_files if os.path.isfile(p)]
    return {"files": files}

@router.get("/sessions/{session_id}/files/{name}")
def download_file(session_id: str, name: str):
    session = SESSIONS.get(session_id)
    if not session: raise HTTPException(404, "не найден")
    target = os.path.join(session.work_dir, name)
    if not os.path.isfile(target): raise HTTPException(404, "файл не найден")
    return FileResponse(target, filename=name)

@router.get("/sessions/{session_id}/archive.zip")
def download_zip(session_id: str):
    session = SESSIONS.get(session_id)
    if not session: raise HTTPException(404, "не найден")
    mem = io.BytesIO()
    with zipfile.ZipFile(mem, "w", zipfile.ZIP_DEFLATED) as zf:
        for p in session.result_files:
            if os.path.isfile(p):
                zf.write(p, arcname=os.path.basename(p))
        zf.writestr("status.json", json.dumps(session.to_status().model_dump(), ensure_ascii=False, indent=2))
    mem.seek(0)
    return StreamingResponse(mem, media_type="application/zip",
                             headers={"Content-Disposition": f'attachment; filename=\"%s.zip\"' % session.session_id})

@router.delete("/sessions/{session_id}")
def delete_session(session_id: str):
    from shutil import rmtree
    session = SESSIONS.pop(session_id, None)
    if not session: raise HTTPException(404, "не найден")
    rmtree(session.work_dir, ignore_errors=True)
    return {"deleted": session_id}
