from html import escape
from pathlib import Path
import sys

from fastapi import FastAPI, Form, HTTPException
from fastapi.responses import HTMLResponse, RedirectResponse


BASE_DIR = Path(__file__).resolve().parent
PROJECT_DIR = BASE_DIR.parent
ML_DIR = PROJECT_DIR / "ml"

if str(ML_DIR) not in sys.path:
    sys.path.append(str(ML_DIR))

from database import (  # noqa: E402
    delete_prediction_result,
    get_database_label,
    get_prediction_result,
    init_db,
    list_prediction_results,
    save_prediction_result,
    update_prediction_result,
)


app = FastAPI(title="ConfiVoice Admin Web")


@app.on_event("startup")
def startup():
    init_db()


@app.get("/")
def read_root():
    return {
        "name": "ConfiVoice Admin Web",
        "status": "ok",
        "admin_page": "/admin",
        "database": get_database_label(),
    }


@app.get("/predictions")
def read_predictions(limit: int = 500):
    return {
        "database": get_database_label(),
        "data": list_prediction_results(limit=limit),
    }


@app.post("/admin/predictions")
def create_prediction_from_admin(
    student_name: str = Form(...),
    predicted_label: str = Form("PD"),
    description: str = Form(""),
    confidence_percent: float = Form(0),
    probability_pd_percent: float = Form(0),
    probability_tpd_percent: float = Form(0),
    is_valid_audio: str = Form("1"),
    error_message: str = Form(""),
    audio_duration: float = Form(0),
):
    student_name = student_name.strip()
    if not student_name:
        raise HTTPException(status_code=400, detail="Nama siswa/i wajib diisi.")

    save_prediction_result(
        student_name,
        _result_payload(
            predicted_label=predicted_label,
            description=description,
            confidence_percent=confidence_percent,
            probability_pd_percent=probability_pd_percent,
            probability_tpd_percent=probability_tpd_percent,
            is_valid_audio=is_valid_audio,
            error_message=error_message,
            audio_duration=audio_duration,
        ),
    )
    return _redirect_admin()


@app.get("/admin/predictions/{prediction_id}/edit", response_class=HTMLResponse)
def read_edit_prediction_page(prediction_id: int):
    row = get_prediction_result(prediction_id)
    if row is None:
        raise HTTPException(status_code=404, detail="Data tidak ditemukan.")

    return _html_page(
        title=f"Edit Data #{prediction_id}",
        content=f"""
        <section class="panel">
          <div class="panel-head">
            <div>
              <h2>Edit Data #{prediction_id}</h2>
              <p>Ubah hasil analisis yang tersimpan di admin.</p>
            </div>
            <a class="button secondary" href="/admin">Kembali</a>
          </div>
          {_prediction_form(row=row, action=f"/admin/predictions/{prediction_id}/edit", submit_label="Simpan Perubahan")}
        </section>
        """,
    )


@app.post("/admin/predictions/{prediction_id}/edit")
def update_prediction_from_admin(
    prediction_id: int,
    student_name: str = Form(...),
    predicted_label: str = Form("PD"),
    description: str = Form(""),
    confidence_percent: float = Form(0),
    probability_pd_percent: float = Form(0),
    probability_tpd_percent: float = Form(0),
    is_valid_audio: str = Form("1"),
    error_message: str = Form(""),
    audio_duration: float = Form(0),
):
    student_name = student_name.strip()
    if not student_name:
        raise HTTPException(status_code=400, detail="Nama siswa/i wajib diisi.")

    updated = update_prediction_result(
        prediction_id,
        _db_values(
            student_name=student_name,
            predicted_label=predicted_label,
            description=description,
            confidence_percent=confidence_percent,
            probability_pd_percent=probability_pd_percent,
            probability_tpd_percent=probability_tpd_percent,
            is_valid_audio=is_valid_audio,
            error_message=error_message,
            audio_duration=audio_duration,
        ),
    )
    if not updated:
        raise HTTPException(status_code=404, detail="Data tidak ditemukan.")

    return _redirect_admin()


@app.post("/admin/predictions/{prediction_id}/delete")
def delete_prediction_from_admin(prediction_id: int):
    delete_prediction_result(prediction_id)
    return _redirect_admin()


@app.get("/admin", response_class=HTMLResponse)
def read_admin_page():
    rows = list_prediction_results(limit=500)
    total = len(rows)
    pd_count = sum(1 for row in rows if row["predicted_label"] == "PD")
    tpd_count = sum(1 for row in rows if row["predicted_label"] == "TPD")
    avg_pd = sum(float(row["probability_pd"] or 0) for row in rows) / total if total else 0

    return _html_page(
        title="Admin ConfiVoice",
        content=f"""
        <header>
          <div>
            <h1>Admin ConfiVoice</h1>
            <p>Dashboard hasil analisis percaya diri siswa/i dari aplikasi Flutter.</p>
          </div>
        </header>
        <section class="stats">
          <div class="stat"><span>Total data</span><strong>{total}</strong></div>
          <div class="stat"><span>Percaya diri</span><strong>{pd_count}</strong></div>
          <div class="stat"><span>Tidak percaya diri</span><strong>{tpd_count}</strong></div>
          <div class="stat"><span>Rata-rata PercayaD</span><strong>{avg_pd * 100:.1f}%</strong></div>
        </section>
        <section class="panel">
          <div class="panel-head">
            <div>
              <h2>Tambah Data</h2>
              <p>Gunakan form ini jika perlu menambahkan hasil analisis secara manual.</p>
            </div>
          </div>
          {_prediction_form(action="/admin/predictions", submit_label="Tambah Data")}
        </section>
        <section class="panel">
          <div class="panel-head">
            <div>
              <h2>Data Analisis</h2>
              <p>Kelola data yang sudah tersimpan.</p>
            </div>
          </div>
          {_prediction_table(rows)}
        </section>
        """,
    )


def _redirect_admin():
    return RedirectResponse(url="/admin", status_code=303)


def _result_payload(
    *,
    predicted_label,
    description,
    confidence_percent,
    probability_pd_percent,
    probability_tpd_percent,
    is_valid_audio,
    error_message,
    audio_duration,
):
    return {
        "predicted_label": predicted_label,
        "description": description,
        "confidence": _percent_to_ratio(confidence_percent),
        "probability_pd": _percent_to_ratio(probability_pd_percent),
        "probability_tpd": _percent_to_ratio(probability_tpd_percent),
        "is_valid_audio": is_valid_audio == "1",
        "error_message": error_message.strip() or None,
        "audio_quality": {"duration": audio_duration},
        "voice_indicators": {},
    }


def _db_values(
    *,
    student_name,
    predicted_label,
    description,
    confidence_percent,
    probability_pd_percent,
    probability_tpd_percent,
    is_valid_audio,
    error_message,
    audio_duration,
):
    return (
        student_name,
        predicted_label.strip() or None,
        description.strip() or None,
        _percent_to_ratio(confidence_percent),
        _percent_to_ratio(probability_pd_percent),
        _percent_to_ratio(probability_tpd_percent),
        1 if is_valid_audio == "1" else 0,
        error_message.strip() or None,
        float(audio_duration or 0),
        0,
        0,
        0,
        0,
        0,
    )


def _percent_to_ratio(value):
    return max(0, min(float(value or 0), 100)) / 100


def _ratio_to_percent(value):
    return f"{float(value or 0) * 100:.2f}"


def _prediction_form(row=None, action="/admin/predictions", submit_label="Simpan"):
    row = row or {}
    student_name = escape(str(row.get("student_name") or ""))
    predicted_label = str(row.get("predicted_label") or "PD")
    description = escape(str(row.get("description") or ""))
    confidence = _ratio_to_percent(row.get("confidence"))
    probability_pd = _ratio_to_percent(row.get("probability_pd"))
    probability_tpd = _ratio_to_percent(row.get("probability_tpd"))
    audio_duration = float(row.get("audio_duration") or 0)
    error_message = escape(str(row.get("error_message") or ""))
    valid_audio = str(int(row.get("is_valid_audio", 1))) == "1"
    pd_selected = "selected" if predicted_label == "PD" else ""
    tpd_selected = "selected" if predicted_label == "TPD" else ""
    valid_selected = "selected" if valid_audio else ""
    invalid_selected = "selected" if not valid_audio else ""

    return f"""
    <form class="form-grid" method="post" action="{escape(action)}">
      <label>
        <span>Nama siswa/i</span>
        <input name="student_name" value="{student_name}" required>
      </label>
      <label>
        <span>Label</span>
        <select name="predicted_label">
          <option value="PD" {pd_selected}>PD</option>
          <option value="TPD" {tpd_selected}>TPD</option>
        </select>
      </label>
      <label>
        <span>Confidence (%)</span>
        <input name="confidence_percent" type="number" min="0" max="100" step="0.01" value="{confidence}">
      </label>
      <label>
        <span>PD (%)</span>
        <input name="probability_pd_percent" type="number" min="0" max="100" step="0.01" value="{probability_pd}">
      </label>
      <label>
        <span>TPD (%)</span>
        <input name="probability_tpd_percent" type="number" min="0" max="100" step="0.01" value="{probability_tpd}">
      </label>
      <label>
        <span>Durasi audio (detik)</span>
        <input name="audio_duration" type="number" min="0" step="0.01" value="{audio_duration:.2f}">
      </label>
      <label>
        <span>Status audio</span>
        <select name="is_valid_audio">
          <option value="1" {valid_selected}>Valid</option>
          <option value="0" {invalid_selected}>Tidak valid</option>
        </select>
      </label>
      <label class="wide">
        <span>Keterangan</span>
        <input name="description" value="{description}">
      </label>
      <label class="wide">
        <span>Pesan error audio</span>
        <input name="error_message" value="{error_message}">
      </label>
      <div class="form-actions">
        <button class="button" type="submit">{escape(submit_label)}</button>
      </div>
    </form>
    """


def _prediction_table(rows):
    table_rows = []
    for row in rows:
        valid_text = "Valid" if row["is_valid_audio"] else "Tidak valid"
        table_rows.append(
            "<tr>"
            f"<td>{row['id']}</td>"
            f"<td>{escape(str(row['created_at']))}</td>"
            f"<td>{escape(row['student_name'])}</td>"
            f"<td><span class='badge'>{escape(row['predicted_label'] or '-')}</span></td>"
            f"<td>{escape(row['description'] or '-')}</td>"
            f"<td>{float(row['confidence'] or 0) * 100:.2f}%</td>"
            f"<td>{float(row['probability_pd'] or 0) * 100:.2f}%</td>"
            f"<td>{float(row['probability_tpd'] or 0) * 100:.2f}%</td>"
            f"<td>{float(row['audio_duration'] or 0):.2f} dtk</td>"
            f"<td>{valid_text}</td>"
            "<td class='row-actions'>"
            f"<a class='small-button' href='/admin/predictions/{row['id']}/edit'>Edit</a>"
            f"<form method='post' action='/admin/predictions/{row['id']}/delete' "
            "onsubmit=\"return confirm('Hapus data ini?');\">"
            "<button class='small-button danger' type='submit'>Hapus</button>"
            "</form>"
            "</td>"
            "</tr>"
        )

    body = "\n".join(table_rows) or (
        '<tr><td colspan="11" class="empty">Belum ada hasil prediksi.</td></tr>'
    )

    return f"""
    <div class="table-wrap">
      <table>
        <thead>
          <tr>
            <th>ID</th>
            <th>Waktu</th>
            <th>Nama siswa/i</th>
            <th>Label</th>
            <th>Keterangan</th>
            <th>Confidence</th>
            <th>PD</th>
            <th>TPD</th>
            <th>Durasi</th>
            <th>Status audio</th>
            <th>Aksi</th>
          </tr>
        </thead>
        <tbody>{body}</tbody>
      </table>
    </div>
    """


def _html_page(title, content):
    return f"""
    <!doctype html>
    <html lang="id">
      <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>{escape(title)}</title>
        <style>
          :root {{
            font-family: Arial, sans-serif;
            background: #f6f8f8;
            color: #17211f;
          }}
          * {{
            box-sizing: border-box;
          }}
          body {{
            margin: 0;
            padding: 24px;
          }}
          header {{
            margin-bottom: 18px;
          }}
          h1, h2 {{
            margin: 0 0 6px;
          }}
          h1 {{
            font-size: 28px;
          }}
          h2 {{
            font-size: 20px;
          }}
          p {{
            margin: 0;
            color: #586966;
          }}
          .stats {{
            display: grid;
            grid-template-columns: repeat(4, minmax(140px, 1fr));
            gap: 12px;
            margin-bottom: 16px;
          }}
          .stat, .panel {{
            background: white;
            border: 1px solid #dde7e4;
            border-radius: 8px;
          }}
          .stat {{
            padding: 14px;
          }}
          .stat span {{
            display: block;
            color: #687976;
            font-size: 13px;
            margin-bottom: 6px;
          }}
          .stat strong {{
            font-size: 24px;
          }}
          .panel {{
            margin-bottom: 16px;
            padding: 16px;
          }}
          .panel-head {{
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 12px;
            margin-bottom: 14px;
          }}
          .form-grid {{
            display: grid;
            grid-template-columns: repeat(4, minmax(160px, 1fr));
            gap: 12px;
          }}
          label span {{
            display: block;
            color: #586966;
            font-size: 13px;
            margin-bottom: 6px;
          }}
          input, select {{
            width: 100%;
            min-height: 40px;
            border: 1px solid #cbd9d6;
            border-radius: 8px;
            padding: 9px 10px;
            font: inherit;
            background: #ffffff;
            color: #17211f;
          }}
          .wide {{
            grid-column: span 2;
          }}
          .form-actions {{
            display: flex;
            align-items: end;
          }}
          .button, .small-button {{
            display: inline-flex;
            align-items: center;
            justify-content: center;
            border: 0;
            border-radius: 8px;
            background: #0f766e;
            color: #ffffff;
            font-weight: 700;
            text-decoration: none;
            cursor: pointer;
          }}
          .button {{
            min-height: 40px;
            padding: 9px 14px;
          }}
          .button:hover, .small-button:hover {{
            background: #115e59;
          }}
          .button.secondary, .small-button {{
            background: #e7f4f1;
            color: #0f766e;
          }}
          .button.secondary:hover, .small-button:hover {{
            background: #d3ebe7;
          }}
          .small-button {{
            min-height: 32px;
            padding: 7px 10px;
            font-size: 13px;
          }}
          .small-button.danger {{
            background: #fee2e2;
            color: #b91c1c;
          }}
          .small-button.danger:hover {{
            background: #fecaca;
          }}
          .table-wrap {{
            overflow-x: auto;
            border: 1px solid #dbe7e4;
            border-radius: 8px;
          }}
          table {{
            width: 100%;
            border-collapse: collapse;
            min-width: 1240px;
          }}
          th, td {{
            padding: 12px 14px;
            border-bottom: 1px solid #e8efed;
            text-align: left;
            white-space: nowrap;
          }}
          th {{
            background: #eef6f4;
            font-size: 13px;
            text-transform: uppercase;
          }}
          tr:last-child td {{
            border-bottom: 0;
          }}
          .badge {{
            display: inline-block;
            min-width: 40px;
            padding: 4px 8px;
            border-radius: 999px;
            background: #e7f4f1;
            color: #0f766e;
            text-align: center;
            font-weight: 700;
          }}
          .empty {{
            text-align: center;
            color: #687a76;
          }}
          .row-actions {{
            display: flex;
            align-items: center;
            gap: 8px;
          }}
          .row-actions form {{
            margin: 0;
          }}
          @media (max-width: 980px) {{
            .form-grid {{
              grid-template-columns: repeat(2, minmax(140px, 1fr));
            }}
            .wide {{
              grid-column: span 2;
            }}
          }}
          @media (max-width: 760px) {{
            body {{
              padding: 14px;
            }}
            .stats {{
              grid-template-columns: repeat(2, minmax(120px, 1fr));
            }}
            .form-grid {{
              grid-template-columns: 1fr;
            }}
            .wide {{
              grid-column: span 1;
            }}
            .panel-head {{
              display: block;
            }}
            .panel-head .button {{
              margin-top: 12px;
            }}
          }}
        </style>
      </head>
      <body>
        {content}
      </body>
    </html>
    """
