"""NCSBFC public website — photo homepage, education pages, agent accounts."""

from __future__ import annotations

import hmac
import os
import secrets
from functools import wraps
from urllib.parse import urlencode

from flask import (
    Flask,
    jsonify,
    redirect,
    render_template,
    request,
    session,
    url_for,
)

import accounts
import config
import leads

app = Flask(__name__)
app.secret_key = config.SECRET_KEY
app.config["TEMPLATES_AUTO_RELOAD"] = True

PRODUCTION = os.environ.get("NCSBFC_ENV", "").lower() == "production" or os.environ.get(
    "FLASK_ENV", ""
).lower() == "production"

app.config.update(
    SESSION_COOKIE_HTTPONLY=True,
    SESSION_COOKIE_SAMESITE="Lax",
    SESSION_COOKIE_SECURE=PRODUCTION,
    PREFERRED_URL_SCHEME="https" if PRODUCTION else "http",
)

FAMILY_NAV = [
    {"label": "Final Expense Insurance", "href": "/for-families/final-expense-insurance"},
    {"label": "Burial Insurance in North Carolina", "href": "/for-families/burial-insurance-north-carolina"},
    {"label": "Funeral Insurance and Pre-Need Plans", "href": "/for-families/funeral-insurance-and-pre-need-plans"},
    {"label": "Understanding Waiting Periods", "href": "/for-families/understanding-waiting-periods"},
    {"label": "How Much Coverage May Be Needed?", "href": "/for-families/how-much-coverage-may-be-needed"},
    {"label": "Questions to Ask", "href": "/for-families/questions-to-ask"},
    {"label": "Final Expense FAQs", "href": "/for-families/faqs"},
    {"label": "Get a Quote", "href": "/quote"},
]


@app.context_processor
def inject_site():
    if "csrf_token" not in session:
        session["csrf_token"] = secrets.token_hex(16)
    user = None
    if session.get("agent_id"):
        user = accounts.find_by_id(session["agent_id"])
        if user:
            user = accounts.public_user(user)
    return {
        "site": config.public_config(),
        "request_path": request.path,
        "family_nav": FAMILY_NAV,
        "csrf_token": session.get("csrf_token", ""),
        "agent_user": user,
    }


def csrf_ok() -> bool:
    sent = request.form.get("csrf_token", "")
    expected = session.get("csrf_token", "")
    return bool(expected) and hmac.compare_digest(sent, expected)


def login_required(view):
    @wraps(view)
    def wrapped(*args, **kwargs):
        if not session.get("agent_id"):
            return redirect(url_for("agent_login", next=request.path))
        return view(*args, **kwargs)

    return wrapped


def utm_from_request() -> dict[str, str]:
    keys = ["utm_source", "utm_medium", "utm_campaign", "utm_content", "utm_term"]
    stored = session.get("utm") or {}
    current = {key: (request.values.get(key) or stored.get(key) or "").strip() for key in keys}
    session["utm"] = current
    return current


def query_preserve():
    return urlencode({k: v for k, v in utm_from_request().items() if v})


def wants_json() -> bool:
    if request.headers.get("X-Requested-With") == "XMLHttpRequest":
        return True
    accept = request.headers.get("Accept", "")
    return "application/json" in accept and "text/html" not in accept.split(",")[0]


def consumer_fields(form):
    return {
        "first_name": form.get("first_name", "").strip(),
        "last_name": form.get("last_name", "").strip(),
        "phone": form.get("phone", "").strip(),
        "email": form.get("email", "").strip(),
        "zip_code": form.get("zip_code", "").strip(),
        "age_range": form.get("age_range", "").strip(),
        "tobacco_use": form.get("tobacco_use", "").strip(),
        "coverage_amount": form.get("coverage_amount", "").strip(),
        "best_time": form.get("best_time", "").strip(),
        "notes": form.get("notes", "").strip(),
    }


def agent_fields(form):
    return {
        "first_name": form.get("first_name", "").strip(),
        "last_name": form.get("last_name", "").strip(),
        "phone": form.get("phone", "").strip(),
        "email": form.get("email", "").strip(),
        "city_state": form.get("city_state", "").strip(),
        "license_status": form.get("license_status", "").strip(),
        "resident_states": form.get("resident_states", "").strip(),
        "years_experience": form.get("years_experience", "").strip(),
        "market_focus": form.get("market_focus", "").strip(),
        "sales_model": form.get("sales_model", "").strip(),
        "support_needed": form.get("support_needed", "").strip(),
        "best_time": form.get("best_time", "").strip(),
    }


@app.before_request
def before():
    utm_from_request()
    if PRODUCTION:
        proto = request.headers.get("X-Forwarded-Proto", request.scheme)
        if proto != "https" and request.endpoint not in ("static",):
            url = request.url.replace("http://", "https://", 1)
            return redirect(url, code=301)
    return None


def handle_consumer_submit():
    fields = consumer_fields(request.form)
    errors = leads.validate_consumer(fields)
    utm = utm_from_request()
    spam, reason = leads.spam_flags(request.form, request.form.get("form_started_at", ""))
    if errors:
        if wants_json():
            return jsonify({"ok": False, "errors": errors}), 400
        return redirect(request.referrer or url_for("quote"))
    landing = request.values.get("landing_page_url") or (config.SITE_URL + request.path)
    referrer = request.values.get("referrer_url") or request.referrer or ""
    path = request.form.get("form_path") or request.path
    source = leads.source_label_for("consumer", path, utm.get("utm_source", ""))
    lead = leads.store_lead(
        form_type="consumer",
        fields=fields,
        utm=utm,
        landing_page_url=landing,
        referrer_url=referrer,
        source_label=source,
        spam=spam,
        spam_reason=reason,
    )
    qs = query_preserve()
    dest = url_for("thank_you") + (("?" + qs) if qs else "")
    if wants_json():
        return jsonify({"ok": True, "redirect": dest, "duplicate": lead["duplicate"], "id": lead["id"]})
    return redirect(dest)


def handle_agent_submit():
    fields = agent_fields(request.form)
    errors = leads.validate_agent(fields)
    utm = utm_from_request()
    spam, reason = leads.spam_flags(request.form, request.form.get("form_started_at", ""))
    if errors:
        if wants_json():
            return jsonify({"ok": False, "errors": errors}), 400
        return redirect(request.referrer or url_for("agents_interest"))
    landing = request.values.get("landing_page_url") or (config.SITE_URL + request.path)
    referrer = request.values.get("referrer_url") or request.referrer or ""
    lead = leads.store_lead(
        form_type="agent",
        fields=fields,
        utm=utm,
        landing_page_url=landing,
        referrer_url=referrer,
        source_label="Agent Interest Lead",
        spam=spam,
        spam_reason=reason,
    )
    qs = query_preserve()
    dest = url_for("agent_thank_you") + (("?" + qs) if qs else "")
    if wants_json():
        return jsonify({"ok": True, "redirect": dest, "duplicate": lead["duplicate"], "id": lead["id"]})
    return redirect(dest)


@app.after_request
def security_headers(response):
    response.headers["X-Content-Type-Options"] = "nosniff"
    response.headers["X-Frame-Options"] = "SAMEORIGIN"
    response.headers["Referrer-Policy"] = "strict-origin-when-cross-origin"
    if PRODUCTION:
        response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
    return response


@app.get("/")
def home():
    return render_template(
        "home.html",
        meta={
            "title": "Protect the People You Love | NCSBFC",
            "description": "A simple way for North Carolina families to request final-expense life-insurance information from an independent insurance agency.",
        },
    )


@app.route("/quote", methods=["GET", "POST"])
def quote():
    if request.method == "POST":
        return handle_consumer_submit()
    return render_template(
        "quote.html",
        meta={
            "title": "Request a Free Quote | NCSBFC",
            "description": "Request no-obligation information about final-expense life-insurance options for North Carolina families.",
        },
    )


@app.route("/for-families/get-a-quote", methods=["GET", "POST"])
def get_a_quote():
    if request.method == "POST":
        return handle_consumer_submit()
    return redirect(url_for("quote"))


@app.route("/api/leads/consumer", methods=["POST"])
def api_consumer():
    return handle_consumer_submit()


@app.route("/api/leads/agent", methods=["POST"])
def api_agent():
    return handle_agent_submit()


@app.get("/thank-you")
def thank_you():
    return render_template(
        "thank_you.html",
        meta={"title": "Thank You | NCSBFC", "noindex": True, "conversion": "consumer"},
    )


@app.get("/about")
def about():
    return render_template(
        "about.html",
        meta={
            "title": "Independent insurance guidance for North Carolina families | NCSBFC",
            "description": "North Carolina Senior Benefits & Family Care is an independent insurance agency serving North Carolina families.",
        },
    )


@app.get("/contact")
def contact():
    return render_template(
        "contact.html",
        meta={
            "title": "Contact NCSBFC",
            "description": "Request a no-obligation conversation with North Carolina Senior Benefits & Family Care.",
        },
        breadcrumbs=[("Home", "/"), ("Contact", None)],
    )


@app.get("/licensing")
def licensing():
    return render_template(
        "licensing.html",
        meta={
            "title": "Licensing & Disclosures | NCSBFC",
            "description": "Licensing and disclosures for North Carolina Senior Benefits & Family Care.",
        },
    )


@app.get("/privacy")
def privacy():
    return render_template(
        "legal.html",
        meta={"title": "Privacy Policy | NCSBFC"},
        page_title="Privacy Policy",
        body="This website collects the information you submit on a form, plus technical details such as the page you visited and advertising parameters, so NCSBFC can respond to your request. We may contact you as described in the form consent.",
    )


@app.get("/terms")
def terms():
    return render_template(
        "legal.html",
        meta={"title": "Terms and Disclosures | NCSBFC"},
        page_title="Terms and Disclosures",
        body=config.LEGAL_DISCLOSURE + " " + config.VARIABILITY_DISCLAIMER,
    )


@app.get("/for-families")
def for_families():
    return render_template(
        "for_families.html",
        meta={
            "title": "Learn About Final-Expense Options | NCSBFC",
            "description": "Educational guides for North Carolina families exploring final-expense life insurance.",
        },
        breadcrumbs=[("Home", "/"), ("Learn More", None)],
    )


@app.get("/for-families/final-expense-insurance")
def page_final_expense():
    return render_template(
        "education/final_expense.html",
        meta={"title": "What Is Final-Expense Life Insurance? | NCSBFC", "description": "A plain-English explanation of final-expense life insurance."},
        breadcrumbs=[("Home", "/"), ("Learn More", "/for-families"), ("Final Expense Insurance", None)],
    )


@app.get("/for-families/burial-insurance-north-carolina")
def page_burial():
    return render_template(
        "education/burial_nc.html",
        meta={"title": "Burial Insurance in North Carolina | NCSBFC", "description": "Burial insurance information for North Carolina families."},
        breadcrumbs=[("Home", "/"), ("Learn More", "/for-families"), ("Burial Insurance", None)],
    )


@app.get("/for-families/funeral-insurance-and-pre-need-plans")
def page_funeral_vs_preneed():
    return render_template(
        "education/funeral_vs_preneed.html",
        meta={"title": "Funeral Insurance vs. Pre-Need Plans | NCSBFC", "description": "An impartial comparison of life insurance and pre-need funeral plans."},
        breadcrumbs=[("Home", "/"), ("Learn More", "/for-families"), ("Funeral Insurance and Pre-Need Plans", None)],
    )


@app.get("/for-families/understanding-waiting-periods")
def page_waiting():
    return render_template(
        "education/waiting_periods.html",
        meta={"title": "Understanding Waiting Periods | NCSBFC", "description": "Waiting periods for final-expense life insurance, explained in plain English."},
        breadcrumbs=[("Home", "/"), ("Learn More", "/for-families"), ("Waiting Periods", None)],
    )


@app.get("/for-families/how-much-coverage-may-be-needed")
def page_coverage_amount():
    return render_template(
        "education/coverage_amount.html",
        meta={"title": "How Much Coverage May Be Needed? | NCSBFC", "description": "An educational worksheet for estimating a possible coverage gap."},
        breadcrumbs=[("Home", "/"), ("Learn More", "/for-families"), ("How Much Coverage May Be Needed?", None)],
    )


@app.get("/for-families/questions-to-ask")
def page_questions():
    return render_template(
        "education/questions.html",
        meta={"title": "Questions to Ask | NCSBFC", "description": "Questions to ask before choosing a final-expense policy."},
        breadcrumbs=[("Home", "/"), ("Learn More", "/for-families"), ("Questions to Ask", None)],
    )


@app.get("/for-families/faqs")
def page_faqs():
    return render_template(
        "education/faqs.html",
        meta={"title": "Final-Expense FAQs | NCSBFC", "description": "Plain-English answers about final-expense life insurance."},
        breadcrumbs=[("Home", "/"), ("Learn More", "/for-families"), ("FAQs", None)],
    )


@app.get("/agents")
def agents():
    return render_template(
        "agents/index.html",
        meta={
            "title": "Work With NCSBFC | Agent Opportunities",
            "description": "Explore whether NCSBFC may be a fit for independent insurance professionals.",
        },
        breadcrumbs=[("Home", "/"), ("For Agents", None)],
    )


@app.get("/agents/why-work-with-us")
def agents_why():
    return render_template(
        "agents/why.html",
        meta={"title": "Why Work With NCSBFC", "description": "What licensed agents may find when exploring work with NCSBFC."},
        breadcrumbs=[("Home", "/"), ("For Agents", "/agents"), ("Why Work With Us", None)],
    )


@app.get("/agents/training-and-resources")
def agents_training():
    return render_template(
        "agents/training.html",
        meta={"title": "Agent Training and Resources | NCSBFC", "description": "Training topics that may be available to independent agents."},
        breadcrumbs=[("Home", "/"), ("For Agents", "/agents"), ("Training and Resources", None)],
    )


@app.get("/agents/faqs")
def agents_faqs():
    return render_template(
        "agents/faqs.html",
        meta={"title": "Agent FAQs | NCSBFC", "description": "Transparent answers for licensed and aspiring agents."},
        breadcrumbs=[("Home", "/"), ("For Agents", "/agents"), ("Agent FAQs", None)],
    )


@app.route("/agents/interest", methods=["GET", "POST"])
def agents_interest():
    if request.method == "POST":
        return handle_agent_submit()
    return render_template(
        "agents/interest.html",
        meta={"title": "Agent Interest Form | NCSBFC", "description": "Request a confidential conversation about agent opportunities."},
        breadcrumbs=[("Home", "/"), ("For Agents", "/agents"), ("Agent Interest Form", None)],
    )


@app.get("/agent-thank-you")
def agent_thank_you():
    return render_template(
        "agents/thank_you.html",
        meta={"title": "Thank You | NCSBFC Agent Interest", "noindex": True, "conversion": "agent"},
    )


@app.route("/agents/register", methods=["GET", "POST"])
def agent_register():
    errors = {}
    if request.method == "POST":
        if not csrf_ok():
            errors["form"] = "Please try again."
        else:
            user, errors = accounts.register(request.form)
            if user:
                session["agent_id"] = user["id"]
                session["csrf_token"] = secrets.token_hex(16)
                return redirect(url_for("agent_account"))
    return render_template(
        "agents/register.html",
        meta={"title": "Create an Agent Account | NCSBFC", "description": "Create an account to work with NCSBFC."},
        errors=errors,
        breadcrumbs=[("Home", "/"), ("For Agents", "/agents"), ("Create Account", None)],
    )


@app.route("/agents/login", methods=["GET", "POST"])
def agent_login():
    error = ""
    if request.method == "POST":
        if not csrf_ok():
            error = "Please try again."
        else:
            attempts = session.get("login_attempts", 0)
            if attempts >= 8:
                error = "Too many sign-in attempts. Please wait and try again later."
            else:
                user = accounts.authenticate(request.form.get("email", ""), request.form.get("password", ""))
                if user:
                    session["agent_id"] = user["id"]
                    session["login_attempts"] = 0
                    session["csrf_token"] = secrets.token_hex(16)
                    nxt = request.values.get("next") or url_for("agent_account")
                    if not nxt.startswith("/"):
                        nxt = url_for("agent_account")
                    return redirect(nxt)
                session["login_attempts"] = attempts + 1
                error = "That email or password did not match our records."
    return render_template(
        "agents/login.html",
        meta={"title": "Agent Sign In | NCSBFC", "description": "Sign in to your NCSBFC agent account."},
        error=error,
        breadcrumbs=[("Home", "/"), ("For Agents", "/agents"), ("Sign In", None)],
    )


@app.get("/agents/logout")
def agent_logout():
    session.pop("agent_id", None)
    return redirect(url_for("home"))


@app.get("/agents/account")
@login_required
def agent_account():
    return render_template(
        "agents/account.html",
        meta={"title": "Agent Account | NCSBFC", "noindex": True},
        breadcrumbs=[("Home", "/"), ("For Agents", "/agents"), ("My Account", None)],
    )


@app.errorhandler(404)
def not_found(_e):
    return render_template("404.html", meta={"title": "Page Not Found | NCSBFC", "noindex": True}), 404


if __name__ == "__main__":
    app.run(host="127.0.0.1", port=5050, debug=not PRODUCTION)
