import os
import json
import requests
from flask import Blueprint, render_template, send_file, redirect, url_for, flash

from app import db
from src.models import Issue, Article, Image
from src.cover import generate_cover
from src.epub_builder import build_epub
import config

issues_bp = Blueprint("issues", __name__)


@issues_bp.route("/issues")
def index():
    issues = Issue.query.order_by(Issue.created_at.desc()).all()
    issue_data = []
    for issue in issues:
        article_count = len(json.loads(issue.article_ids))
        issue_data.append({
            "issue": issue,
            "article_count": article_count,
        })
    return render_template("issues.html", issues=issue_data)


@issues_bp.route("/issues/<int:issue_id>/download")
def download(issue_id):
    issue = db.get_or_404(Issue, issue_id)
    if not os.path.exists(issue.epub_path):
        flash("ePub file not found.", "error")
        return redirect(url_for("issues.index"))
    return send_file(
        issue.epub_path,
        as_attachment=True,
        download_name=os.path.basename(issue.epub_path),
    )


@issues_bp.route("/issues/<int:issue_id>/epub")
def epub_file(issue_id):
    issue = db.get_or_404(Issue, issue_id)
    if not os.path.exists(issue.epub_path):
        flash("ePub file not found.", "error")
        return redirect(url_for("issues.index"))
    return send_file(issue.epub_path, mimetype="application/epub+zip")


@issues_bp.route("/issues/<int:issue_id>/cover")
def cover_image(issue_id):
    issue = db.get_or_404(Issue, issue_id)
    if not issue.cover_path or not os.path.exists(issue.cover_path):
        flash("Cover image not found.", "error")
        return redirect(url_for("issues.index"))
    return send_file(issue.cover_path, mimetype="image/jpeg")


@issues_bp.route("/issues/<int:issue_id>/read")
def read(issue_id):
    issue = db.get_or_404(Issue, issue_id)
    if not os.path.exists(issue.epub_path):
        flash("ePub file not found.", "error")
        return redirect(url_for("issues.index"))

    article_count = len(json.loads(issue.article_ids))
    if issue.issue_type == "single_article":
        article_ids = json.loads(issue.article_ids)
        article = db.session.get(Article, article_ids[0]) if article_ids else None
        title = article.title if article else "Single Article"
    elif issue.issue_type == "multi_week":
        w1 = issue.week_start.isocalendar()[1]
        w2 = issue.week_end.isocalendar()[1]
        title = f"Weeks {w1}\u2013{w2}"
    else:
        title = f"Week {issue.week_start.isocalendar()[1]}"

    return render_template(
        "reader.html",
        issue=issue,
        title=title,
        article_count=article_count,
    )


@issues_bp.route("/issues/<int:issue_id>/delete", methods=["POST"])
def delete(issue_id):
    issue = db.get_or_404(Issue, issue_id)

    if issue.epub_path and os.path.exists(issue.epub_path):
        os.remove(issue.epub_path)
    if issue.cover_path and os.path.exists(issue.cover_path):
        os.remove(issue.cover_path)

    db.session.delete(issue)
    db.session.commit()

    flash("Issue deleted.")
    return redirect(url_for("issues.index"))


@issues_bp.route("/issues/<int:issue_id>/regenerate", methods=["POST"])
def regenerate(issue_id):
    issue = db.get_or_404(Issue, issue_id)
    article_ids = json.loads(issue.article_ids)

    articles_for_issue = (
        Article.query.filter(Article.id.in_(article_ids))
        .order_by(Article.pub_date.asc())
        .all()
    )
    headlines = [
        a.title for a in articles_for_issue
        if "Obituaries" not in json.loads(a.categories)
    ]

    image_paths = []
    for a in articles_for_issue:
        if "Obituaries" in json.loads(a.categories):
            continue
        first_image = Image.query.filter_by(article_id=a.id).first()
        if first_image:
            image_paths.append(first_image.local_path)

    cover_method = issue.cover_method
    if cover_method == "ai":
        issue.cover_method = "mosaic"
        cover_method = "mosaic"

    try:
        cover_path = generate_cover(
            cover_method, config.ISSUES_DIR,
            issue.week_start, issue.week_end, headlines, image_paths
        )
        epub_path = build_epub(
            issue.week_start, issue.week_end, article_ids,
            cover_path, config.ISSUES_DIR
        )

        issue.cover_path = cover_path
        issue.epub_path = epub_path
        db.session.commit()

        flash("Issue regenerated successfully.")
    except Exception as e:
        flash(f"Regeneration failed: {e}", "error")

    return redirect(url_for("issues.index"))


@issues_bp.route("/issues/<int:issue_id>/push", methods=["POST"])
def push(issue_id):
    issue = db.get_or_404(Issue, issue_id)

    if not config.GRIMMORY_URL or not config.GRIMMORY_USERNAME or not config.GRIMMORY_PASSWORD:
        flash("Grimmory/Booklore integration is not configured.", "error")
        return redirect(url_for("issues.index"))

    if not issue.epub_path or not os.path.exists(issue.epub_path):
        flash("ePub file not found.", "error")
        return redirect(url_for("issues.index"))

    try:
        base_url = config.GRIMMORY_URL.rstrip('/')
        
        # 1. Authenticate to get a token
        login_resp = requests.post(
            f"{base_url}/api/v1/auth/login",
            json={"username": config.GRIMMORY_USERNAME, "password": config.GRIMMORY_PASSWORD},
            timeout=10
        )
        
        if login_resp.status_code != 200:
            flash("Failed to authenticate with library. Check username/password.", "error")
            return redirect(url_for("issues.index"))
            
        login_data = login_resp.json()
        token = login_data.get("token") or login_data.get("accessToken") or login_data.get("accessToken_Internal")
        
        if not token and "data" in login_data and isinstance(login_data["data"], dict):
            token = login_data["data"].get("token") or login_data["data"].get("accessToken")
            
        if not token:
            flash("Failed to extract authentication token from library response.", "error")
            return redirect(url_for("issues.index"))

        # 2. Resolve Library ID and Path ID if configured
        data = {}
        if config.GRIMMORY_LIBRARY_ID:
            headers = {'Authorization': f'Bearer {token}'}
            lib_resp = requests.get(f"{base_url}/api/v1/libraries", headers=headers, timeout=10)
            if lib_resp.status_code == 200:
                libraries = lib_resp.json()
                target_lib = None
                
                # Try to find by ID first, then by name
                for lib in libraries:
                    if str(lib.get('id')) == str(config.GRIMMORY_LIBRARY_ID) or lib.get('name', '').lower() == str(config.GRIMMORY_LIBRARY_ID).lower():
                        target_lib = lib
                        break
                
                if target_lib:
                    data['libraryId'] = target_lib['id']
                    if target_lib.get('paths') and len(target_lib['paths']) > 0:
                        data['pathId'] = target_lib['paths'][0]['id']
                    else:
                        flash(f"Library '{target_lib['name']}' has no paths configured.", "error")
                        return redirect(url_for("issues.index"))
                else:
                    flash(f"Could not find library matching '{config.GRIMMORY_LIBRARY_ID}'.", "error")
                    return redirect(url_for("issues.index"))
            else:
                flash(f"Failed to fetch libraries from server. Status: {lib_resp.status_code}", "error")
                return redirect(url_for("issues.index"))

        # 3. Upload the file to the library
        files = {
            'file': (
                os.path.basename(issue.epub_path),
                open(issue.epub_path, 'rb'),
                'application/epub+zip'
            )
        }

        headers = {'Authorization': f'Bearer {token}'}
        
        response = requests.post(
            f"{base_url}/api/v1/files/upload",
            files=files,
            data=data,
            headers=headers,
            timeout=30
        )

        if response.status_code in (200, 201, 204):
            flash("Issue successfully pushed to library.")
        elif response.status_code == 409:
            flash("Issue was already pushed to the library previously.")
        else:
            flash(f"Failed to push issue. Status: {response.status_code}. {response.text}", "error")

    except requests.RequestException as e:
        flash(f"Error connecting to library server: {e}", "error")
    except Exception as e:
        flash(f"An unexpected error occurred: {e}", "error")

    return redirect(url_for("issues.index"))
