# ============================================================
# File: _solve_ode_boundary_value_problems_sturm_liouville.py
# Topic: ODE Boundary Value Problems (BVPs) — Sturm–Liouville / Eigenvalues / Modes
#
# We implement app-friendly canonical BVP solvers (closed-form eigenpairs):
#
# A) y'' + λ y = 0 on [0,L]
#    1) Dirichlet–Dirichlet: y(0)=0, y(L)=0
#       => λ_n = (nπ/L)^2,  y_n = sin(nπ x/L), n=1,2,3,...
#
#    2) Neumann–Neumann: y'(0)=0, y'(L)=0
#       => λ_0=0 with y_0=1 (constant),
#          λ_n=(nπ/L)^2 with y_n=cos(nπ x/L), n=1,2,3,...
#
#    3) Dirichlet–Neumann: y(0)=0, y'(L)=0
#       => λ_n=((2n+1)π/(2L))^2, y_n=sin((2n+1)π x/(2L)), n=0,1,2,...
#
#    4) Neumann–Dirichlet: y'(0)=0, y(L)=0
#       => λ_n=((2n+1)π/(2L))^2, y_n=cos((2n+1)π x/(2L)), n=0,1,2,...
#
# B) Orthogonality (weight w=1):
#      ∫_0^L y_m(x) y_n(x) dx = 0 for m≠n
#    and norms:
#      ∫_0^L sin^2(nπ x/L) dx = L/2
#      ∫_0^L cos^2(nπ x/L) dx = L/2  for n>=1
#      ∫_0^L 1^2 dx = L   (Neumann n=0 mode)
#
# C) (Optional concept hook) Green's function statement (not constructing full G here):
#      For Ly=f with BCs: y(x)=∫_0^L G(x,ξ) f(ξ) dξ
#
# Notes:
#   - We keep to the most standard Sturm–Liouville engine to avoid numeric root finding.
#   - Output: one randomly selected mode index n and its eigenpair + orthogonality note.
# ============================================================

from __future__ import annotations

from typing import Optional, Union, Dict, Any, Literal

import sympy as sp


BvpKind = Literal[
    "dd",
    "nn",
    "dn",
    "nd",
]


def solve_ode_boundary_value_problems(
    *,
    kind: BvpKind,
    # parameters
    L_: Union[int, float, str, sp.Expr] = sp.pi,
    n: Union[int, float, str, sp.Expr] = 1,
    # symbols
    x_symbol: str = "x",
    y_symbol: str = "y",
    lambda_symbol: str = "lambda",
    locals_map: Optional[Dict[str, Any]] = None,
    simplify: bool = True,
) -> dict:
    """
    Solve canonical Sturm–Liouville eigenvalue BVP:
        y'' + λ y = 0 on [0,L]
    with one of the BC types.

    Returns:
        { "problem_s": "...", "soln_s": "...", "answer_s": "..." }
    """
    x = sp.Symbol(x_symbol, real=True)
    y = sp.Function(y_symbol)
    yx = y(x)

    lam = sp.Symbol(lambda_symbol, real=True)

    base_locals = {
        x_symbol: x,
        y_symbol: y,
        lambda_symbol: lam,
        "pi": sp.pi,
        "E": sp.E,
        "exp": sp.exp,
        "log": sp.log,
        "ln": sp.log,
        "sqrt": sp.sqrt,
        "sin": sp.sin,
        "cos": sp.cos,
        "tanh": sp.tanh,
        "sinh": sp.sinh,
        "cosh": sp.cosh,
        "Abs": sp.Abs,
    }
    if locals_map:
        base_locals.update(locals_map)

    def P(v: Union[int, float, str, sp.Expr]) -> sp.Expr:
        return sp.sympify(v, locals=base_locals)

    def Ltx(expr: sp.Expr) -> str:
        return sp.latex(expr)

    L = P(L_)
    N = P(n)

    if simplify:
        L = sp.simplify(L)
        N = sp.simplify(N)

    # ODE text
    # FIX: Replaced '0<x<' with '0 \\lt x \\lt' to avoid HTML parsing errors
    ode_ltx = "y'' + \\lambda y = 0,\\quad 0 \\lt x \\lt " + Ltx(L)

    # Compute eigenpair templates
    # Note: choose indexing conventions consistent with the BC family.
    lam_n = None
    y_n = None
    bc_ltx = ""
    n_label = ""  # display n index set

    if kind == "dd":
        # n=1,2,3,...
        bc_ltx = "y(0)=0,\\; y(" + Ltx(L) + ")=0"
        n_label = "n=1,2,3,\\dots"
        lam_n = (N * sp.pi / L) ** 2
        y_n = sp.sin(N * sp.pi * x / L)

    elif kind == "nn":
        # n=0,1,2,... with n=0 special
        bc_ltx = "y'(0)=0,\\; y'(" + Ltx(L) + ")=0"
        n_label = "n=0,1,2,\\dots"
        # If user passes n=0, return constant eigenfunction
        if simplify and sp.simplify(N) == 0:
            lam_n = sp.Integer(0)
            y_n = sp.Integer(1)
        else:
            lam_n = (N * sp.pi / L) ** 2
            y_n = sp.cos(N * sp.pi * x / L)

    elif kind == "dn":
        # n=0,1,2,...
        bc_ltx = "y(0)=0,\\; y'(" + Ltx(L) + ")=0"
        n_label = "n=0,1,2,\\dots"
        k = (2 * N + 1) * sp.pi / (2 * L)
        lam_n = k**2
        y_n = sp.sin(k * x)

    elif kind == "nd":
        # n=0,1,2,...
        bc_ltx = "y'(0)=0,\\; y(" + Ltx(L) + ")=0"
        n_label = "n=0,1,2,\\dots"
        k = (2 * N + 1) * sp.pi / (2 * L)
        lam_n = k**2
        y_n = sp.cos(k * x)

    else:
        raise ValueError("Unknown kind={!r}. Use 'dd','nn','dn','nd'.".format(kind))

    if simplify:
        lam_n = sp.simplify(lam_n)
        y_n = sp.simplify(y_n)

    # Norm / orthogonality notes (weight = 1)
    # give norm for selected family
    norm_expr = None
    if kind == "nn" and (simplify and sp.simplify(N) == 0):
        norm_expr = sp.integrate(1, (x, 0, L))  # = L
    elif y_n.has(sp.sin):
        norm_expr = sp.integrate(sp.sin(sp.Wild("a") * x) ** 2, (x, 0, L))
        # safer explicit:
        norm_expr = sp.integrate(y_n**2, (x, 0, L))
    elif y_n.has(sp.cos):
        norm_expr = sp.integrate(y_n**2, (x, 0, L))
    else:
        norm_expr = sp.integrate(y_n**2, (x, 0, L))

    if simplify:
        norm_expr = sp.simplify(norm_expr)

    # Build HTML (avoid f-string brace issues; use concatenation)
    # FIX: Replaced > and < with \\gt and \\lt to avoid HTML issues
    soln_s = (
        "<div class='wrap'><div class='card'>"
        "<div class='badge'>ODE Boundary Value Problem — Sturm–Liouville Eigenmodes<br/></div>"
        "\\(\\displaystyle \\begin{aligned}"
        "& \\displaystyle " + ode_ltx + " \\\\[6pt]"
        "& \\displaystyle \\text{BC: } " + bc_ltx + " \\\\[10pt]"
        "& \\displaystyle \\Rightarrow \\text{Solve by cases in }\\lambda\\text{ (} \\lambda \\gt 0,\\lambda=0,\\lambda \\lt 0\\text{), then apply BCs} \\\\[6pt]"
        "& \\displaystyle \\Rightarrow \\text{Nontrivial solution requires a quantization condition on }\\lambda \\\\[10pt]"
        "& \\displaystyle \\Rightarrow \\text{Eigenvalues: } \\lambda_" + Ltx(N) + " = " + Ltx(lam_n) + ",\\; " + n_label + " \\\\[6pt]"
        "& \\displaystyle \\Rightarrow \\text{Eigenfunction (mode): } y_" + Ltx(N) + "(" + Ltx(x) + ") = " + Ltx(y_n) + " \\\\[10pt]"
        "& \\displaystyle \\Rightarrow \\text{Orthogonality (weight }w=1\\text{): } \\int_0^{" + Ltx(L) + "} y_m(x)\\,y_n(x)\\,dx = 0\\; (m\\ne n) \\\\[6pt]"
        "& \\displaystyle \\Rightarrow \\text{Norm of this mode: } \\int_0^{" + Ltx(L) + "} \\left(y_" + Ltx(N) + "(x)\\right)^2 dx = " + Ltx(norm_expr) + " \\\\[4pt]"
        "\\end{aligned}\\)"
        "<div class='note'>For forced BVPs \\(Ly=f\\) with these BCs, a Green's function \\(G(x,\\xi)\\) gives "
        "\\(y(x)=\\int_0^L G(x,\\xi)f(\\xi)d\\xi\\).</div>"
        "</div></div>"
    )

    problem_s = "Find eigenvalues/eigenfunctions for \\( " + ode_ltx + " \\) with \\( " + bc_ltx + " \\)."

    answer_s = (
        "\\( \\lambda_" + Ltx(N) + " = " + Ltx(lam_n) + ",\\; "
        + "y_" + Ltx(N) + "(" + Ltx(x) + ") = " + Ltx(y_n) + " \\)"
    )

    return {"problem_s": problem_s.strip(), "soln_s": soln_s.strip(), "answer_s": answer_s.strip()}