from pyscript import window, document
import sympy as sp
from sympy.parsing.sympy_parser import (
    parse_expr, standard_transformations,
    implicit_multiplication_application, convert_xor
)

x = sp.Symbol('x', real=True)
transformations = standard_transformations + (implicit_multiplication_application, convert_xor)

def parse_math(expr_str):
    if not expr_str:
        return sp.S.Zero
    expr_str = str(expr_str).strip()
    if not expr_str:
        return sp.S.Zero
    expr_str = expr_str.replace('÷', '/').replace('×', '*').replace('−', '-').replace('–', '-')
    try:
        return parse_expr(expr_str, transformations=transformations, local_dict={'x': x})
    except Exception:
        raise ValueError(f"خطا در خواندن عبارت «{expr_str}». لطفاً ساختار عبارت ریاضی را بررسی کنید.")

def render_katex(element_id, latex_str, display=True):
    el = document.getElementById(element_id)
    if el:
        try:
            window.katex.render(latex_str, el, {"displayMode": display, "throwOnError": False})
        except Exception:
            el.innerText = latex_str

def solve_rational(event=None):
    try:
        err_el = document.getElementById("error-box")
        if err_el:
            err_el.hidden = True
            err_el.innerText = ""

        mode = document.getElementById("mode-select").value
        steps = []
        factors = []

        n1_val = document.getElementById("num1").value
        d1_val = document.getElementById("den1").value
        n1_expr = parse_math(n1_val)
        d1_expr = parse_math(d1_val)

        if mode in ["add", "sub", "mul", "div"]:
            n2_val = document.getElementById("num2").value
            d2_val = document.getElementById("den2").value
            n2_expr = parse_math(n2_val)
            d2_expr = parse_math(d2_val)

        if mode == "simplify":
            if d1_expr == 0:
                raise ValueError("مخرج کسر نمی‌تواند صفر باشد (عبارت تعریف‌نشده است).")

            orig_latex = rf"\frac{{{sp.latex(n1_expr)}}}{{{sp.latex(d1_expr)}}}"
            steps.append(("عبارت اولیه", rf"P(x) = {orig_latex}"))

            n1_fact = sp.factor(n1_expr)
            d1_fact = sp.factor(d1_expr)

            steps.append(("تجزیه صورت و مخرج به عوامل اول", rf"P(x) = \frac{{{sp.latex(n1_fact)}}}{{{sp.latex(d1_fact)}}}"))
            factors.append(("تجزیه صورت کسر", rf"{sp.latex(n1_expr)} = {sp.latex(n1_fact)}"))
            factors.append(("تجزیه مخرج کسر", rf"{sp.latex(d1_expr)} = {sp.latex(d1_fact)}"))

            simplified = sp.cancel(n1_expr / d1_expr)
            final_latex = sp.latex(simplified)

            steps.append(("ساده‌سازی و حذف عوامل مشترک", rf"P(x) = {final_latex}"))

            render_katex("final-result-katex", rf"P(x) = {final_latex}")
            update_domain(sp.solve(d1_expr, x))

        elif mode == "add":
            if d1_expr == 0 or d2_expr == 0:
                raise ValueError("مخرج هیچ‌یک از کسرها نمی‌تواند صفر باشد.")

            orig_latex = rf"\frac{{{sp.latex(n1_expr)}}}{{{sp.latex(d1_expr)}}} + \frac{{{sp.latex(n2_expr)}}}{{{sp.latex(d2_expr)}}}"
            steps.append(("عبارت اولیه", rf"P(x) = {orig_latex}"))

            d1_f = sp.factor(d1_expr)
            d2_f = sp.factor(d2_expr)
            factors.append(("مخرج کسر اول", rf"{sp.latex(d1_expr)} = {sp.latex(d1_f)}"))
            factors.append(("مخرج کسر دوم", rf"{sp.latex(d2_expr)} = {sp.latex(d2_f)}"))

            lcm_den = sp.lcm(d1_expr, d2_expr)
            lcm_f = sp.factor(lcm_den)
            steps.append(("تعیین مخرج مشترک (ک.م.م مخرج‌ها)", rf"\text{{LCM}} = {sp.latex(lcm_f)}"))

            m1 = sp.cancel(lcm_den / d1_expr)
            m2 = sp.cancel(lcm_den / d2_expr)

            comb_n = n1_expr * m1 + n2_expr * m2
            steps.append(("هم‌مخرج‌سازی و جمع صورت‌ها", rf"P(x) = \frac{{({sp.latex(n1_expr)})({sp.latex(m1)}) + ({sp.latex(n2_expr)})({sp.latex(m2)})}}{{{sp.latex(lcm_f)}}}"))

            exp_n = sp.expand(comb_n)
            steps.append(("بسط و ساده‌سازی صورت عبارت", rf"P(x) = \frac{{{sp.latex(exp_n)}}}{{{sp.latex(lcm_f)}}}"))

            fact_n = sp.factor(exp_n)
            if fact_n != exp_n:
                steps.append(("تجزیه صورت جدید کسر", rf"P(x) = \frac{{{sp.latex(fact_n)}}}{{{sp.latex(lcm_f)}}}"))

            final_expr = sp.cancel(comb_n / lcm_den)
            final_latex = sp.latex(final_expr)
            steps.append(("ساده‌سازی نهایی کسر حاصل", rf"P(x) = {final_latex}"))

            render_katex("final-result-katex", rf"P(x) = {final_latex}")
            update_domain(set(sp.solve(d1_expr, x) + sp.solve(d2_expr, x)))

        elif mode == "sub":
            if d1_expr == 0 or d2_expr == 0:
                raise ValueError("مخرج هیچ‌یک از کسرها نمی‌تواند صفر باشد.")

            orig_latex = rf"\frac{{{sp.latex(n1_expr)}}}{{{sp.latex(d1_expr)}}} - \frac{{{sp.latex(n2_expr)}}}{{{sp.latex(d2_expr)}}}"
            steps.append(("عبارت اولیه", rf"P(x) = {orig_latex}"))

            d1_f = sp.factor(d1_expr)
            d2_f = sp.factor(d2_expr)
            factors.append(("مخرج کسر اول", rf"{sp.latex(d1_expr)} = {sp.latex(d1_f)}"))
            factors.append(("مخرج کسر دوم", rf"{sp.latex(d2_expr)} = {sp.latex(d2_f)}"))

            lcm_den = sp.lcm(d1_expr, d2_expr)
            lcm_f = sp.factor(lcm_den)
            steps.append(("تعیین مخرج مشترک (ک.م.م مخرج‌ها)", rf"\text{{LCM}} = {sp.latex(lcm_f)}"))

            m1 = sp.cancel(lcm_den / d1_expr)
            m2 = sp.cancel(lcm_den / d2_expr)

            comb_n = n1_expr * m1 - n2_expr * m2
            steps.append(("هم‌مخرج‌سازی و تفریق صورت‌ها", rf"P(x) = \frac{{({sp.latex(n1_expr)})({sp.latex(m1)}) - ({sp.latex(n2_expr)})({sp.latex(m2)})}}{{{sp.latex(lcm_f)}}}"))

            exp_n = sp.expand(comb_n)
            steps.append(("بسط و ساده‌سازی صورت عبارت", rf"P(x) = \frac{{{sp.latex(exp_n)}}}{{{sp.latex(lcm_f)}}}"))

            fact_n = sp.factor(exp_n)
            if fact_n != exp_n:
                steps.append(("تجزیه صورت جدید کسر", rf"P(x) = \frac{{{sp.latex(fact_n)}}}{{{sp.latex(lcm_f)}}}"))

            final_expr = sp.cancel(comb_n / lcm_den)
            final_latex = sp.latex(final_expr)
            steps.append(("ساده‌سازی نهایی کسر حاصل", rf"P(x) = {final_latex}"))

            render_katex("final-result-katex", rf"P(x) = {final_latex}")
            update_domain(set(sp.solve(d1_expr, x) + sp.solve(d2_expr, x)))

        elif mode == "mul":
            if d1_expr == 0 or d2_expr == 0:
                raise ValueError("مخرج کسرها نمی‌تواند صفر باشد.")

            orig_latex = rf"\frac{{{sp.latex(n1_expr)}}}{{{sp.latex(d1_expr)}}} \cdot \frac{{{sp.latex(n2_expr)}}}{{{sp.latex(d2_expr)}}}"
            steps.append(("عبارت اولیه", rf"P(x) = {orig_latex}"))

            n1_f, d1_f = sp.factor(n1_expr), sp.factor(d1_expr)
            n2_f, d2_f = sp.factor(n2_expr), sp.factor(d2_expr)
            factors.append(("صورت کسر اول", rf"{sp.latex(n1_expr)} = {sp.latex(n1_f)}"))
            factors.append(("مخرج کسر اول", rf"{sp.latex(d1_expr)} = {sp.latex(d1_f)}"))
            factors.append(("صورت کسر دوم", rf"{sp.latex(n2_expr)} = {sp.latex(n2_f)}"))
            factors.append(("مخرج کسر دوم", rf"{sp.latex(d2_expr)} = {sp.latex(d2_f)}"))

            steps.append(("تجزیه فاکتورهای صورت و مخرج", rf"P(x) = \frac{{{sp.latex(n1_f)}}}{{{sp.latex(d1_f)}}} \cdot \frac{{{sp.latex(n2_f)}}}{{{sp.latex(d2_f)}}}"))

            eff_n = n1_expr * n2_expr
            eff_d = d1_expr * d2_expr
            steps.append(("ضرب صورت در صورت و مخرج در مخرج", rf"P(x) = \frac{{({sp.latex(n1_f)})({sp.latex(n2_f)})}}{{({sp.latex(d1_f)})({sp.latex(d2_f)})}}"))

            final_expr = sp.cancel(eff_n / eff_d)
            final_latex = sp.latex(final_expr)
            steps.append(("ساده‌سازی نهایی و حذف عوامل مشترک", rf"P(x) = {final_latex}"))

            render_katex("final-result-katex", rf"P(x) = {final_latex}")
            update_domain(set(sp.solve(d1_expr, x) + sp.solve(d2_expr, x)))

        elif mode == "div":
            if d1_expr == 0 or d2_expr == 0:
                raise ValueError("مخرج کسرها نمی‌تواند صفر باشد.")
            if n2_expr == 0:
                raise ValueError("در تقسیم، صورت کسر دوم (که معکوس می‌شود) نمی‌تواند صفر باشد.")

            orig_latex = rf"\frac{{{sp.latex(n1_expr)}}}{{{sp.latex(d1_expr)}}} \div \frac{{{sp.latex(n2_expr)}}}{{{sp.latex(d2_expr)}}}"
            steps.append(("عبارت اولیه", rf"P(x) = {orig_latex}"))

            steps.append(("تبدیل تقسیم به ضرب در معکوس کسر دوم", rf"P(x) = \frac{{{sp.latex(n1_expr)}}}{{{sp.latex(d1_expr)}}} \cdot \frac{{{sp.latex(d2_expr)}}}{{{sp.latex(n2_expr)}}}"))

            n1_f, d1_f = sp.factor(n1_expr), sp.factor(d1_expr)
            n2_f, d2_f = sp.factor(n2_expr), sp.factor(d2_expr)
            factors.append(("صورت کسر اول", rf"{sp.latex(n1_expr)} = {sp.latex(n1_f)}"))
            factors.append(("مخرج کسر اول", rf"{sp.latex(d1_expr)} = {sp.latex(d1_f)}"))
            factors.append(("صورت کسر دوم", rf"{sp.latex(n2_expr)} = {sp.latex(n2_f)}"))
            factors.append(("مخرج کسر دوم", rf"{sp.latex(d2_expr)} = {sp.latex(d2_f)}"))

            steps.append(("تجزیه فاکتورهای صورت و مخرج", rf"P(x) = \frac{{{sp.latex(n1_f)}}}{{{sp.latex(d1_f)}}} \cdot \frac{{{sp.latex(d2_f)}}}{{{sp.latex(n2_f)}}}"))

            eff_n = n1_expr * d2_expr
            eff_d = d1_expr * n2_expr
            final_expr = sp.cancel(eff_n / eff_d)
            final_latex = sp.latex(final_expr)
            steps.append(("ساده‌سازی نهایی و حذف عوامل مشترک", rf"P(x) = {final_latex}"))

            render_katex("final-result-katex", rf"P(x) = {final_latex}")
            update_domain(set(sp.solve(d1_expr, x) + sp.solve(d2_expr, x) + sp.solve(n2_expr, x)))

        elif mode == "long_div":
            if d1_expr == 0:
                raise ValueError("مقسوم‌علیه (مخرج) نمی‌تواند صفر باشد.")

            try:
                p_num = sp.Poly(n1_expr, x)
                p_den = sp.Poly(d1_expr, x)
            except Exception:
                raise ValueError("ورودی‌ها جهت تقسیم چکشی باید چندجمله‌ای بر حسب x باشند.")

            curr_expr = n1_expr
            quo_accum = sp.S.Zero
            step_cnt = 1
            diagram_rows = []

            div_latex = sp.latex(d1_expr)

            while True:
                try:
                    curr_poly = sp.Poly(curr_expr, x)
                    den_poly = sp.Poly(d1_expr, x)
                except Exception:
                    break

                if curr_expr == 0 or curr_poly.degree() < den_poly.degree():
                    break

                lt_curr_monomial = curr_poly.monoms()[0]
                lt_curr_coeff = curr_poly.coeffs()[0]
                lt_curr_expr = lt_curr_coeff * (x ** lt_curr_monomial[0])

                lt_den_monomial = den_poly.monoms()[0]
                lt_den_coeff = den_poly.coeffs()[0]
                lt_den_expr = lt_den_coeff * (x ** lt_den_monomial[0])

                term_expr = sp.simplify(lt_curr_expr / lt_den_expr)
                mult_expr = sp.expand(term_expr * d1_expr)
                next_curr_expr = sp.expand(curr_expr - mult_expr)

                steps.append((
                    f"گام {step_cnt}: تقسیم بزرگ‌ترین جمله مقسوم بر بزرگ‌ترین جمله مقسوم‌علیه",
                    rf"\frac{{{sp.latex(lt_curr_expr)}}}{{{sp.latex(lt_den_expr)}}} = {sp.latex(term_expr)}"
                ))

                steps.append((
                    f"ضرب جمله حاصل ({sp.latex(term_expr)}) در مقسوم‌علیه و تفریق آن از عبارت قبلی",
                    rf"({sp.latex(curr_expr)}) - ({sp.latex(mult_expr)}) = {sp.latex(next_curr_expr)}"
                ))

                diagram_rows.append({
                    'curr': sp.latex(curr_expr),
                    'mult': sp.latex(mult_expr),
                    'term': sp.latex(term_expr)
                })

                quo_accum = quo_accum + term_expr
                curr_expr = next_curr_expr
                step_cnt += 1

            rem_expr = curr_expr
            quo_latex = sp.latex(quo_accum)
            rem_latex = sp.latex(rem_expr)

            array_lines = []
            array_lines.append(rf"{sp.latex(n1_expr)} & {div_latex} \\ \hline")

            if diagram_rows:
                for idx, row in enumerate(diagram_rows):
                    q_cell = quo_latex if idx == 0 else ""
                    if idx > 0:
                        array_lines.append(rf"{row['curr']} & \\")
                    array_lines.append(rf"-({row['mult']}) & {q_cell} \\ \hline")
                array_lines.append(rf"{rem_latex} &")
            else:
                array_lines.append(rf"& {quo_latex} \\ \hline")
                array_lines.append(rf"{rem_latex} &")

            diagram_latex = r"\begin{array}{r|l} " + " ".join(array_lines) + r" \end{array}"

            steps.insert(0, ("نمودار کامل تقسیم چکشی ", diagram_latex))

            factors.append(("مقسوم (Dividend)", rf"A(x) = {sp.latex(n1_expr)}"))
            factors.append(("مقسوم‌علیه (Divisor)", rf"B(x) = {sp.latex(d1_expr)}"))
            factors.append(("خارج‌قسمت (Quotient)", rf"Q(x) = {quo_latex}"))
            factors.append(("باقی‌مانده (Remainder)", rf"R(x) = {rem_latex}"))

            if rem_expr == 0:
                division_result = rf"{quo_latex}"
                steps.append(("رابطه بازگشتی و نتیجه نهایی", rf"{sp.latex(n1_expr)} = ({div_latex})({quo_latex})"))
            else:
                division_result = rf"{quo_latex} + \frac{{{rem_latex}}}{{{div_latex}}}"
                steps.append(("رابطه بازگشتی تقسیم", rf"{sp.latex(n1_expr)} = ({div_latex})({quo_latex}) + ({rem_latex})"))

            render_katex("final-result-katex", rf"\frac{{{sp.latex(n1_expr)}}}{{{sp.latex(d1_expr)}}} = {division_result}")
            update_domain(sp.solve(d1_expr, x))

        render_steps(steps)
        render_factors(factors)

    except Exception as e:
        show_error(str(e))

def update_domain(roots):
    dom_el = document.getElementById("domain-restrictions-box")
    valid_roots = []
    for r in roots:
        try:
            valid_roots.append(sp.latex(r))
        except Exception:
            valid_roots.append(str(r))

    if valid_roots:
        dom_str = r", \quad ".join([rf"x \neq {r}" for r in valid_roots])
        render_katex("domain-katex", dom_str)
        if dom_el:
            dom_el.hidden = False
    else:
        render_katex("domain-katex", r"x \in \mathbb{R} \quad \text{(بدون ریشه حقیقی برای مخرج)}")
        if dom_el:
            dom_el.hidden = False

def render_steps(steps):
    container = document.getElementById("steps-container")
    if not container:
        return
    container.innerHTML = ""
    for i, (title, latex_str) in enumerate(steps):
        step_div = document.createElement("div")
        step_div.className = "step-item"

        title_div = document.createElement("div")
        title_div.className = "step-text"
        title_div.innerText = title

        math_div = document.createElement("div")
        math_div.className = "math-box ltr"
        math_id = f"step-math-{i}"
        math_div.id = math_id

        step_div.appendChild(title_div)
        step_div.appendChild(math_div)
        container.appendChild(step_div)

        render_katex(math_id, latex_str)

def render_factors(factors):
    container = document.getElementById("factors-container")
    if not container:
        return
    container.innerHTML = ""
    if not factors:
        container.innerHTML = "اطلاعات اجزای تقسیم موجود نیست."
        return
    for i, (title, latex_str) in enumerate(factors):
        item_div = document.createElement("div")
        item_div.className = "step-item"

        title_div = document.createElement("div")
        title_div.className = "step-text"
        title_div.innerText = title

        math_div = document.createElement("div")
        math_div.className = "math-box ltr"
        math_id_str = f"factor-math-{i}"
        math_div.id = math_id_str

        item_div.appendChild(title_div)
        item_div.appendChild(math_div)
        container.appendChild(item_div)

        render_katex(math_id_str, latex_str)

def show_error(msg):
    err_el = document.getElementById("error-box")
    if err_el:
        err_el.innerText = f"خطا: {msg}"
        err_el.hidden = False
    render_katex("final-result-katex", r"\text{خطا در محاسبات}")

window.solve_rational_py = solve_rational

btn_solve = document.getElementById("btn-solve")
if btn_solve:
    btn_solve.disabled = False

solve_rational()