# plugins/importer/routes.py

import csv
import io
import difflib

from datetime import datetime
from flask import Blueprint, request, render_template, redirect, flash, session, url_for
from flask_login import login_required, current_user
from flask_wtf.csrf import generate_csrf

from app.neo4j_utils import get_neo4j_handler
from plugins.plant.models import (
    db,
    Plant,
    PlantCommonName,
    PlantScientificName,
    PlantOwnershipLog
)

bp = Blueprint('importer', __name__, template_folder='templates', url_prefix='/import')

# ────────────────────────────────────────────────────────────────────────────────
# Redirect “/import/” → “/import/upload”
# ────────────────────────────────────────────────────────────────────────────────

@bp.route("/", methods=["GET"])
@login_required
def index():
    # When someone hits /import, send them to /import/upload
    return redirect(url_for("importer.upload"))


# ────────────────────────────────────────────────────────────────────────────────
# Required CSV headers for import
# ────────────────────────────────────────────────────────────────────────────────
REQUIRED_HEADERS = {"uuid", "plant_type", "name", "scientific_name", "mother_uuid"}


@bp.route("/upload", methods=["GET", "POST"])
@login_required
def upload():
    if request.method == "POST":
        file = request.files.get("file")
        if not file:
            flash("No file selected", "error")
            return redirect(request.url)

        # Decode as UTF-8-SIG to strip any BOM, then parse with csv.DictReader
        try:
            stream = io.StringIO(file.stream.read().decode("utf-8-sig"))
            reader = csv.DictReader(stream)
        except Exception:
            flash("Failed to read CSV file. Ensure it is valid UTF-8.", "error")
            return redirect(request.url)

        headers = set(reader.fieldnames or [])
        missing = REQUIRED_HEADERS - headers
        if missing:
            flash(f"Missing required CSV headers: {missing}", "error")
            return redirect(request.url)

        # Prepare session storage for the rows under review
        session["pending_rows"] = []
        review_list = []

        # Preload existing common/scientific names (lowercased keys for fuzzy matching)
        all_common = {c.name.lower(): c for c in PlantCommonName.query.all()}
        all_scientific = {s.name.lower(): s for s in PlantScientificName.query.all()}

        for row in reader:
            uuid_raw    = row.get("uuid", "")
            uuid        = uuid_raw.strip().strip('"')

            name_raw    = row.get("name", "")
            name        = name_raw.strip()

            sci_raw     = row.get("scientific_name", "")
            sci_name    = sci_raw.strip()

            plant_type  = row.get("plant_type", "").strip() or "plant"

            mother_raw  = row.get("mother_uuid", "")
            mother_uuid = mother_raw.strip().strip('"')

            # Skip any row where required fields are missing
            if not (uuid and name and plant_type):
                continue

            # ─── If the scientific name doesn’t match exactly, suggest a close match ─────
            # Only suggest if the “closest key” differs from the raw input:
            suggestions = difflib.get_close_matches(
                sci_name.lower(),
                list(all_scientific.keys()),
                n=1,
                cutoff=0.8
            )
            if suggestions and suggestions[0] != sci_name.lower():
                suggested = all_scientific[suggestions[0]].name
            else:
                suggested = None

            review_item = {
                "uuid":        uuid,
                "name":        name,
                "sci_name":    sci_name,
                "suggested":   suggested,
                "plant_type":  plant_type,
                "mother_uuid": mother_uuid
            }
            review_list.append(review_item)
            session["pending_rows"].append(review_item)

        session["review_list"] = review_list
        return redirect(url_for("importer.review"))

    # GET → show upload form
    return render_template("importer/upload.html", csrf_token=generate_csrf())


@bp.route("/review", methods=["GET", "POST"])
@login_required
def review():
    rows        = session.get("pending_rows", [])
    review_list = session.get("review_list", [])

    if request.method == "POST":
        neo   = get_neo4j_handler()
        added = 0

        # Re-load preload maps to avoid NameError if used below
        all_common = {c.name.lower(): c for c in PlantCommonName.query.all()}
        all_scientific = {s.name.lower(): s for s in PlantScientificName.query.all()}

        for row in rows:
            uuid        = row.get("uuid")
            name        = row.get("name")
            sci_name    = row.get("sci_name")
            suggested   = row.get("suggested")
            plant_type  = row.get("plant_type")
            mother_uuid = row.get("mother_uuid")

            # Check if user clicked "confirm" for a suggested scientific name
            accepted_key = f"confirm_{uuid}"
            accepted = request.form.get(accepted_key)

            # ─── MySQL: PlantCommonName ────────────────────────────────────────────────
            common = PlantCommonName.query.filter_by(name=name).first()
            if not common:
                common = PlantCommonName(name=name)
                db.session.add(common)
                db.session.flush()
                all_common[common.name.lower()] = common
            else:
                all_common[common.name.lower()] = common

            # ─── MySQL: PlantScientificName ───────────────────────────────────────────
            sci_to_use = suggested if (suggested and accepted) else sci_name
            scientific = PlantScientificName.query.filter_by(name=sci_to_use).first()
            if not scientific:
                scientific = PlantScientificName(
                    name      = sci_to_use,
                    common_id = common.id
                )
                db.session.add(scientific)
                db.session.flush()
                all_scientific[scientific.name.lower()] = scientific
            else:
                all_scientific[scientific.name.lower()] = scientific

            # ─── Decide if this plant’s data is “verified” by the user ────────────────
            data_verified = False
            if (not suggested) or (suggested and accepted):
                data_verified = True

            # ─── MySQL: Plant record ─────────────────────────────────────────────────
            plant = Plant.query.filter_by(uuid=uuid).first()
            if not plant:
                plant = Plant(
                    uuid           = uuid,
                    common_id      = common.id,
                    scientific_id  = scientific.id,
                    plant_type     = plant_type,
                    owner_id       = current_user.id,
                    data_verified  = data_verified
                )
                db.session.add(plant)
                db.session.flush()  # so plant.id is now available

                log = PlantOwnershipLog(
                    plant_id      = plant.id,
                    user_id       = current_user.id,
                    date_acquired = datetime.utcnow(),
                    transferred   = False,
                    is_verified   = data_verified
                )
                db.session.add(log)
                added += 1
            else:
                # Skip duplicates if the same UUID already exists
                pass

            # ─── Neo4j: ensure the Plant node exists ─────────────────────────────────
            neo.create_plant_node(uuid, name)

            # ─── Neo4j: create a LINEAGE relationship if mother_uuid was provided ─────
            if mother_uuid:
                # Replace the old call with the correct method name:
                neo.create_lineage(child_uuid=uuid, parent_uuid=mother_uuid)

        # Commit all MySQL changes at once
        db.session.commit()
        neo.close()

        flash(f"{added} plants added (MySQL) and Neo4j nodes/relationships created.", "success")

        session.pop("pending_rows", None)
        session.pop("review_list", None)
        return redirect(url_for("importer.upload"))

    # GET → re-render the review page with the same review_list
    return render_template(
        "importer/review.html",
        review_list=review_list,
        csrf_token=generate_csrf()
    )
