"""
Telugu Newspaper Article Extractor — local web app.

Pipeline:
  1. User uploads a PDF.
  2. App renders each page to PNG at a target pixel size (~4200x7400).
  3. PaddleOCR (PP-Structure) finds layout regions per page.
  4. Grouping function joins regions into articles, anchored on the
     "<place>, మే <day> (ఆంధ్రజ్యోతి):" dateline pattern.
  5. Each article is cropped to its own PNG.
  6. Each article's text is OCR'd with Tesseract (telugu).
  7. The user can browse results in the browser and download a zip.

Run:
  pip install -r requirements.txt
  python app.py
  -> open http://localhost:5000
"""
import io
import os
import re
import json
import shutil
import zipfile
import tempfile
import threading
import traceback
from pathlib import Path
from datetime import datetime

from flask import (
    Flask, request, render_template, redirect, url_for,
    send_from_directory, send_file, abort, jsonify,
)

import sys
if hasattr(sys.stdout, 'reconfigure'):
    sys.stdout.reconfigure(encoding='utf-8')
if hasattr(sys.stderr, 'reconfigure'):
    sys.stderr.reconfigure(encoding='utf-8')

from extractor import process_pdf



ROOT = Path(__file__).parent
UPLOAD_DIR = ROOT / "uploads"
OUTPUT_DIR = ROOT / "output"
UPLOAD_DIR.mkdir(exist_ok=True)
OUTPUT_DIR.mkdir(exist_ok=True)

app = Flask(__name__)
app.config["MAX_CONTENT_LENGTH"] = 200 * 1024 * 1024  # 200 MB upload cap


@app.route("/")
def index():
    # list existing jobs newest-first
    jobs = []
    for d in sorted(OUTPUT_DIR.iterdir(), reverse=True):
        if d.is_dir():
            meta_path = d / "meta.json"
            meta = {}
            if meta_path.exists():
                try:
                    meta = json.loads(meta_path.read_text())
                except Exception:
                    pass
            jobs.append({
                "id": d.name,
                "filename": meta.get("filename", "?"),
                "status": meta.get("status", "unknown"),
                "pages": meta.get("pages", 0),
                "articles": meta.get("articles", 0),
                "created": meta.get("created", ""),
            })
    return render_template("index.html", jobs=jobs)


@app.route("/upload", methods=["POST"])
def upload():
    f = request.files.get("pdf")
    if not f or not f.filename.lower().endswith(".pdf"):
        return "Upload a PDF file", 400

    try:
        target_w = int(request.form.get("width", "4200"))
        target_h = int(request.form.get("height", "7400"))
    except ValueError:
        target_w, target_h = 4200, 7400

    # Job folder
    job_id = datetime.now().strftime("%Y%m%d_%H%M%S")
    job_dir = OUTPUT_DIR / job_id
    job_dir.mkdir()

    pdf_path = job_dir / "input.pdf"
    f.save(str(pdf_path))

    # Write initial meta
    meta = {
        "id": job_id,
        "filename": f.filename,
        "status": "queued",
        "target_width": target_w,
        "target_height": target_h,
        "pages": 0,
        "articles": 0,
        "created": datetime.now().isoformat(timespec="seconds"),
    }
    (job_dir / "meta.json").write_text(json.dumps(meta, indent=2))

    # Run in background thread so UI stays responsive
    t = threading.Thread(
        target=_run_job,
        args=(job_id, str(pdf_path), str(job_dir), target_w, target_h),
        daemon=True,
    )
    t.start()

    return redirect(url_for("job_view", job_id=job_id))


def _run_job(job_id, pdf_path, job_dir, target_w, target_h):
    try:
        meta_path = Path(job_dir) / "meta.json"
        meta = json.loads(meta_path.read_text())
        meta["status"] = "running"
        meta["message"] = "Starting..."
        meta_path.write_text(json.dumps(meta, indent=2))
        
        result = process_pdf(pdf_path, job_dir, target_w, target_h, job_id)
        meta_path = Path(job_dir) / "meta.json"
        meta = json.loads(meta_path.read_text())
        meta.update({
            "status": "done",
            "pages": result["pages"],
            "articles": result["articles"],
        })
        meta["status"] = "done"
        meta["message"] = "Finished."
        meta_path.write_text(json.dumps(meta, indent=2))
    except Exception as e:
        traceback.print_exc()
        meta_path = Path(job_dir) / "meta.json"
        try:
            meta = json.loads(meta_path.read_text())
            meta["status"] = "error"
            meta["error"] = str(e)
            meta["message"] = str(e)
            meta_path.write_text(json.dumps(meta, indent=2))
        except Exception:
            pass


@app.route("/job/<job_id>")
def job_view(job_id):
    job_dir = OUTPUT_DIR / job_id
    if not job_dir.exists():
        abort(404)

    meta = json.loads((job_dir / "meta.json").read_text())

    # collect article info if available
    articles = []
    for art_dir in sorted((job_dir / "articles").glob("*")) if (job_dir / "articles").exists() else []:
        if art_dir.is_dir():
            info_path = art_dir / "info.json"
            info = {}
            if info_path.exists():
                info = json.loads(info_path.read_text())
            articles.append({
                "name": art_dir.name,
                "page": info.get("page", "?"),
                "headline_preview": info.get("headline_preview", ""),
                "dateline": info.get("dateline", ""),
                "grouped_by": info.get("grouped_by", ""),
                "has_image": (art_dir / "article.png").exists(),
                "has_text": (art_dir / "article.txt").exists(),
            })

    return render_template("job.html", meta=meta, job_id=job_id, articles=articles)


@app.route("/job/<job_id>/status")
def job_status(job_id):
    meta_path = OUTPUT_DIR / job_id / "meta.json"
    status_data = {"status": "unknown"}
    if meta_path.exists():
        try:
            meta = json.loads(meta_path.read_text())
            status_data["status"] = meta.get("status", "unknown")
            status_data["message"] = meta.get("message", "")
            status_data["meta"] = meta
        except Exception:
            pass
    return jsonify(status_data)


@app.route("/job/<job_id>/page/<int:page_num>")
def page_image(job_id, page_num):
    page_path = OUTPUT_DIR / job_id / "pages" / f"page_{page_num:03d}.png"
    if not page_path.exists():
        abort(404)
    return send_file(str(page_path))


@app.route("/job/<job_id>/article/<art_name>/image")
def article_image(job_id, art_name):
    p = OUTPUT_DIR / job_id / "articles" / art_name / "article.png"
    if not p.exists():
        abort(404)
    return send_file(str(p))


@app.route("/job/<job_id>/article/<art_name>/text")
def article_text(job_id, art_name):
    p = OUTPUT_DIR / job_id / "articles" / art_name / "article.txt"
    if not p.exists():
        return "(no text)", 404
    return p.read_text(encoding="utf-8"), 200, {"Content-Type": "text/plain; charset=utf-8"}


@app.route("/job/<job_id>/download")
def download_zip(job_id):
    job_dir = OUTPUT_DIR / job_id
    if not job_dir.exists():
        abort(404)

    buf = io.BytesIO()
    with zipfile.ZipFile(buf, "w", zipfile.ZIP_DEFLATED) as zf:
        for root, dirs, files in os.walk(job_dir):
            # skip the source pdf and big page images to keep the zip lean
            for fn in files:
                if fn == "input.pdf":
                    continue
                full = Path(root) / fn
                rel = full.relative_to(job_dir)
                zf.write(full, arcname=str(rel))
    buf.seek(0)
    return send_file(
        buf,
        mimetype="application/zip",
        as_attachment=True,
        download_name=f"{job_id}_articles.zip",
    )


if __name__ == "__main__":
    print("Telugu Article Extractor")
    print("Open http://localhost:5000")
    app.run(host="127.0.0.1", port=5000, debug=False, threaded=True)
