"""Flask application — recipe importer web UI."""

import hashlib
import os
import secrets
import traceback

from flask import Flask, render_template, request, redirect, url_for, flash, jsonify, send_from_directory, Response

from app import config
from app.scraper import scrape, supported_sites
from app.mealie import MealieClient
from app.tandoor import TandoorClient

app = Flask(
    __name__,
    template_folder=os.path.join(os.path.dirname(__file__), "templates"),
    static_folder=os.path.join(os.path.dirname(__file__), "static"),
)
app.secret_key = os.environ.get("SECRET_KEY", "recipe-importer-dev-key")

VERSION = os.environ.get("VERSION", "dev")


# ---------------------------------------------------------------------------
# Basic auth middleware
# ---------------------------------------------------------------------------

_AUTH_EXEMPT = {"/health", "/assets/logo.svg", "/assets/logo_notext.svg"}


@app.before_request
def _check_basic_auth():
    """Enforce HTTP Basic Auth if configured."""
    if request.path in _AUTH_EXEMPT or request.path.startswith("/assets/"):
        return None
    cfg = config.load()
    auth_user = cfg.get("auth_username", "").strip()
    auth_hash = cfg.get("auth_password_hash", "").strip()
    if not auth_user or not auth_hash:
        return None  # auth not configured — allow all

    auth = request.authorization
    if (auth
            and auth.type == "basic"
            and auth.username == auth_user
            and _hash_password(auth.password) == auth_hash):
        return None  # credentials match

    return Response(
        "Hitelesítés szükséges.", 401,
        {"WWW-Authenticate": 'Basic realm="Recept Importáló"'},
    )


def _hash_password(password: str) -> str:
    """SHA-256 hex digest of password."""
    return hashlib.sha256(password.encode("utf-8")).hexdigest()


_REDIRECT_PATTERNS = ["ide kattintva", "oldalán találod", "teljes recept itt",
                      "kattints ide", "eredeti recept"]


def _is_redirect_instructions(instructions: list[str]) -> bool:
    """Check if instructions are just a redirect to another site."""
    if len(instructions) > 2:
        return False
    text = " ".join(instructions).lower()
    return any(p in text for p in _REDIRECT_PATTERNS)


# ---------------------------------------------------------------------------
# Routes
# ---------------------------------------------------------------------------


@app.route("/")
def index():
    """Redirect to the import page (or settings if not configured)."""
    cfg = config.load()
    has_mealie = bool(cfg.get("mealie_url") and cfg.get("mealie_api_key"))
    has_tandoor = bool(cfg.get("tandoor_url") and cfg.get("tandoor_api_key"))
    if not has_mealie and not has_tandoor:
        return redirect(url_for("settings"))
    return redirect(url_for("import_page"))


@app.route("/settings", methods=["GET", "POST"])
def settings():
    """Configure Mealie connection."""
    cfg = config.load()

    if request.method == "POST":
        cfg["mealie_url"] = request.form.get("mealie_url", "").strip().rstrip("/")
        cfg["mealie_api_key"] = request.form.get("mealie_api_key", "").strip()
        cfg["tandoor_url"] = request.form.get("tandoor_url", "").strip().rstrip("/")
        cfg["tandoor_api_key"] = request.form.get("tandoor_api_key", "").strip()

        # Auth settings
        new_user = request.form.get("auth_username", "").strip()
        new_pass = request.form.get("auth_password", "").strip()
        if new_user:
            cfg["auth_username"] = new_user
            if new_pass:
                # Only update password hash if a new password was provided
                cfg["auth_password_hash"] = _hash_password(new_pass)
        else:
            # Clear auth if username removed
            cfg.pop("auth_username", None)
            cfg.pop("auth_password_hash", None)

        config.save(cfg)
        flash("Beállítások mentve.", "success")
        return redirect(url_for("settings"))

    return render_template("settings.html", cfg=cfg, version=VERSION)


@app.route("/settings/test", methods=["POST"])
def settings_test():
    """AJAX endpoint — test Mealie connection using form values."""
    url = (request.form.get("mealie_url") or "").strip().rstrip("/")
    key = (request.form.get("mealie_api_key") or "").strip()
    if not url or not key:
        return jsonify({"ok": False, "error": "Nincs megadva Mealie URL vagy API kulcs."})
    try:
        client = MealieClient(url, key, api_url=config.MEALIE_INTERNAL_URL)
        info = client.test_connection()
        return jsonify({"ok": True, "data": info})
    except Exception as exc:
        return jsonify({"ok": False, "error": str(exc)})


@app.route("/settings/test-tandoor", methods=["POST"])
def settings_test_tandoor():
    """AJAX endpoint — test Tandoor connection using form values."""
    url = (request.form.get("tandoor_url") or "").strip().rstrip("/")
    key = (request.form.get("tandoor_api_key") or "").strip()
    if not url or not key:
        return jsonify({"ok": False, "error": "Nincs megadva Tandoor URL vagy API kulcs."})
    try:
        client = TandoorClient(url, key, api_url=config.TANDOOR_INTERNAL_URL)
        info = client.test_connection()
        return jsonify({"ok": True, "data": info})
    except Exception as exc:
        return jsonify({"ok": False, "error": str(exc)})


@app.route("/import", methods=["GET"])
def import_page():
    """Show the import form."""
    cfg = config.load()
    has_mealie = bool(cfg.get("mealie_url") and cfg.get("mealie_api_key"))
    has_tandoor = bool(cfg.get("tandoor_url") and cfg.get("tandoor_api_key"))
    if not has_mealie and not has_tandoor:
        flash("Először állíts be legalább egy szolgáltatást (Mealie vagy Tandoor).", "warning")
        return redirect(url_for("settings"))
    return render_template("import.html", cfg=cfg, version=VERSION,
                           has_mealie=has_mealie, has_tandoor=has_tandoor,
                           supported_sites=supported_sites())


@app.route("/scrape", methods=["POST"])
def scrape_url():
    """AJAX — scrape a recipe URL and return structured data."""
    url = request.form.get("url", "").strip()
    if not url:
        return jsonify({"ok": False, "error": "Nincs URL megadva."})
    try:
        data = scrape(url)

        # Check for duplicates in Mealie and Tandoor
        duplicate = None
        tandoor_duplicate = None
        cfg = config.load()
        if cfg.get("mealie_url") and cfg.get("mealie_api_key"):
            try:
                client = MealieClient(cfg["mealie_url"], cfg["mealie_api_key"],
                                      api_url=config.MEALIE_INTERNAL_URL)
                duplicate = client.find_duplicate(url, data.get("title", ""))
            except Exception:
                pass  # non-fatal
        if cfg.get("tandoor_url") and cfg.get("tandoor_api_key"):
            try:
                client = TandoorClient(cfg["tandoor_url"], cfg["tandoor_api_key"],
                                       api_url=config.TANDOOR_INTERNAL_URL)
                tandoor_duplicate = client.find_duplicate(url, data.get("title", ""))
            except Exception:
                pass  # non-fatal

        # Validate completeness
        warnings = []
        real_ingredients = [i for i in data.get("ingredients", []) if "group" not in i]
        if not real_ingredients:
            warnings.append("A recept nem tartalmaz hozzávalókat.")
        instructions = data.get("instructions", [])
        if not instructions:
            warnings.append("A recept nem tartalmaz elkészítési lépéseket.")
        elif _is_redirect_instructions(instructions):
            warnings.append("Az elkészítés egy másik oldalra mutat. "
                            "A recept valószínűleg nem tartalmaz valódi lépéseket.")

        return jsonify({"ok": True, "data": data, "duplicate": duplicate,
                        "tandoor_duplicate": tandoor_duplicate,
                        "warnings": warnings})
    except Exception as exc:
        return jsonify({"ok": False, "error": str(exc), "trace": traceback.format_exc()})


@app.route("/send", methods=["POST"])
def send_to_mealie():
    """AJAX — send edited recipe data to Mealie."""
    cfg = config.load()
    if not cfg.get("mealie_url") or not cfg.get("mealie_api_key"):
        return jsonify({"ok": False, "error": "Mealie nincs beállítva."})

    payload = request.get_json(silent=True)
    if not payload:
        return jsonify({"ok": False, "error": "Érvénytelen kérés."})

    try:
        client = MealieClient(cfg["mealie_url"], cfg["mealie_api_key"],
                              api_url=config.MEALIE_INTERNAL_URL)
        slug = client.create_recipe(payload)
        recipe_url = f"{cfg['mealie_url']}/g/home/r/{slug}"
        return jsonify({"ok": True, "slug": slug, "url": recipe_url})
    except Exception as exc:
        return jsonify({"ok": False, "error": str(exc), "trace": traceback.format_exc()})


@app.route("/send-tandoor", methods=["POST"])
def send_to_tandoor():
    """AJAX — send edited recipe data to Tandoor."""
    cfg = config.load()
    if not cfg.get("tandoor_url") or not cfg.get("tandoor_api_key"):
        return jsonify({"ok": False, "error": "Tandoor nincs beállítva."})

    payload = request.get_json(silent=True)
    if not payload:
        return jsonify({"ok": False, "error": "Érvénytelen kérés."})

    try:
        client = TandoorClient(cfg["tandoor_url"], cfg["tandoor_api_key"],
                                api_url=config.TANDOOR_INTERNAL_URL)
        result = client.create_recipe(payload)
        return jsonify({"ok": True, "id": result["id"], "url": result["url"]})
    except Exception as exc:
        return jsonify({"ok": False, "error": str(exc), "trace": traceback.format_exc()})


@app.route("/tags", methods=["GET"])
def list_all_tags():
    """Return existing tags from Mealie and Tandoor for autocomplete."""
    cfg = config.load()
    mealie_tags = []
    tandoor_tags = []
    if cfg.get("mealie_url") and cfg.get("mealie_api_key"):
        try:
            client = MealieClient(cfg["mealie_url"], cfg["mealie_api_key"],
                                  api_url=config.MEALIE_INTERNAL_URL)
            mealie_tags = [t["name"] for t in client.list_tags()]
        except Exception:
            pass
    if cfg.get("tandoor_url") and cfg.get("tandoor_api_key"):
        try:
            client = TandoorClient(cfg["tandoor_url"], cfg["tandoor_api_key"],
                                   api_url=config.TANDOOR_INTERNAL_URL)
            tandoor_tags = [t["name"] for t in client.list_keywords()]
        except Exception:
            pass
    return jsonify({"mealie": mealie_tags, "tandoor": tandoor_tags})


# ---------------------------------------------------------------------------
# Assets
# ---------------------------------------------------------------------------

ASSETS_DIR = os.path.join(os.path.dirname(__file__), "assets")


@app.route("/assets/<path:filename>")
def serve_asset(filename):
    """Serve static assets (logo, etc.) from app/assets/."""
    return send_from_directory(ASSETS_DIR, filename)


# ---------------------------------------------------------------------------
# Health
# ---------------------------------------------------------------------------


@app.route("/health")
def health():
    return jsonify({"status": "ok", "version": VERSION})
