{{template "base" .}}

{{define "title"}}{{.Issue.Title}}{{end}}

{{define "body"}}
<div style="margin-bottom:16px;">
    <a href="/issues" class="link">&larr; Zurück</a>
</div>

<div class="card" style="margin-bottom:24px;">
    <div class="card-title" style="font-size:22px;">{{.Issue.Title}}</div>
    <div class="card-text" style="margin-top:8px;">{{.Issue.Description}}</div>
    <div style="font-size:13px; color:var(--text-muted); margin-top:8px;">
        {{.Issue.StartTime.Format "02.01.2006"}} – {{.Issue.EndTime.Format "02.01.2006"}}
    </div>
    {{if hasPermission "issues:write"}}
    <div style="display:flex; gap:8px; margin-top:16px; flex-wrap:wrap;">
        <button class="btn btn--danger"
                hx-delete="/issues/{{.Issue.ID}}"
                hx-confirm="Abstimmung wirklich löschen?">Löschen</button>
    </div>
    <details style="margin-top:16px; border-top:1px solid var(--border); padding-top:16px;">
        <summary style="cursor:pointer; font-weight:600; list-style:none;">Bearbeiten</summary>
        <form hx-patch="/issues/{{.Issue.ID}}" style="margin-top:12px;">
            <div class="form-group">
                <label class="form-label">Titel</label>
                <input class="form-input" name="title" type="text" value="{{.Issue.Title}}" required>
            </div>
            <div class="form-group">
                <label class="form-label">Beschreibung</label>
                <textarea class="form-input" name="description" rows="3">{{.Issue.Description}}</textarea>
            </div>
            <div class="form-group">
                <label class="form-label">Beginn</label>
                <div style="display:flex; gap:8px;">
                    <input class="form-input" name="start_date" type="date"
                           value="{{.Issue.StartTime.Format "2006-01-02"}}" style="flex:1; min-width:0;">
                    {{template "time-input" (dict "name" "start_clock" "value" (.Issue.StartTime.Format "15:04"))}}
                </div>
            </div>
            <div class="form-group">
                <label class="form-label">Ende</label>
                <div style="display:flex; gap:8px;">
                    <input class="form-input" name="end_date" type="date"
                           value="{{.Issue.EndTime.Format "2006-01-02"}}" style="flex:1; min-width:0;">
                    {{template "time-input" (dict "name" "end_clock" "value" (.Issue.EndTime.Format "15:04"))}}
                </div>
            </div>
            <button class="btn btn--primary" type="submit">Speichern</button>
        </form>
    </details>
    {{end}}
</div>

<h2 style="margin-bottom:16px;">Optionen</h2>

{{if .Issue.CanVote}}
<div class="card" style="margin-bottom:24px;">
    <p style="margin-bottom:12px; font-weight:600;">Ihre Stimme abgeben</p>
    {{range .Issue.Options}}
    <label style="display:flex; align-items:center; gap:8px; margin-bottom:8px; cursor:pointer;">
        <input type="radio" name="vote-option" value="{{.ID}}">
        <span>{{.Label}}</span>
    </label>
    {{end}}
    <div id="vote-status" style="font-size:13px; margin-top:8px;"></div>
    <button class="btn btn--primary" style="margin-top:12px;" onclick="castVote()">Abstimmen</button>
</div>
{{end}}

<div class="card-grid">
    {{range .Issue.Options}}
    <div class="card">
        <div class="card-title">{{.Label}}</div>
        <div style="font-size:28px; font-weight:700; margin-top:8px;">{{.VoteCount}}</div>
        <div style="font-size:13px; color:var(--text-muted);">Stimmen</div>
    </div>
    {{end}}
</div>

<script>
const issueID = {{.Issue.ID}};

// ── Blind-signature voting ──────────────────────────────────────────────────

function modPow(base, exp, mod) {
    if (mod === 1n) return 0n;
    let result = 1n;
    base = base % mod;
    while (exp > 0n) {
        if (exp % 2n === 1n) result = (result * base) % mod;
        exp >>= 1n;
        base = (base * base) % mod;
    }
    return result;
}

function modInverse(a, m) {
    let [old_r, r] = [a, m];
    let [old_s, s] = [1n, 0n];
    while (r !== 0n) {
        const q = old_r / r;
        [old_r, r] = [r, old_r - q * r];
        [old_s, s] = [s, old_s - q * s];
    }
    return ((old_s % m) + m) % m;
}

function bigIntToBase64(n) {
    let hex = n.toString(16);
    if (hex.length % 2) hex = "0" + hex;
    const bytes = new Uint8Array(hex.length / 2);
    for (let i = 0; i < bytes.length; i++)
        bytes[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
    return btoa(String.fromCharCode(...bytes));
}

function base64ToBigInt(b64) {
    const bin = atob(b64);
    let hex = "";
    for (let i = 0; i < bin.length; i++)
        hex += bin.charCodeAt(i).toString(16).padStart(2, "0");
    return BigInt("0x" + hex);
}

function bytesToBase64(bytes) {
    return btoa(String.fromCharCode(...bytes));
}

async function castVote() {
    const selected = document.querySelector('input[name="vote-option"]:checked');
    if (!selected) { alert("Bitte eine Option auswählen."); return; }
    const optionID = parseInt(selected.value);
    const status = document.getElementById("vote-status");
    status.textContent = "Wird verarbeitet…";

    try {
        // 1. Get public key
        const pkRes = await fetch(`/issues/${issueID}/pubkey`);
        if (!pkRes.ok) throw new Error("Öffentlicher Schlüssel nicht abrufbar.");
        const { public_key } = await pkRes.json();
        const N = BigInt("0x" + public_key.n);
        const e = BigInt(public_key.e);

        // 2. Generate nonce and compute vote message: SHA-256(issueID||optionID||nonce)
        const nonce = crypto.getRandomValues(new Uint8Array(32));
        const msgBuf = new Uint8Array(16 + nonce.length);
        const view = new DataView(msgBuf.buffer);
        view.setBigUint64(0, BigInt(issueID), false);
        view.setBigUint64(8, BigInt(optionID), false);
        msgBuf.set(nonce, 16);
        const hashBuf = await crypto.subtle.digest("SHA-256", msgBuf);
        const hashHex = Array.from(new Uint8Array(hashBuf))
            .map(b => b.toString(16).padStart(2, "0")).join("");
        const m = BigInt("0x" + hashHex);

        // 3. Pick random blinding factor r in [2, N-1]
        let r;
        do {
            const rb = crypto.getRandomValues(new Uint8Array(256));
            r = BigInt("0x" + Array.from(rb).map(b => b.toString(16).padStart(2, "0")).join(""));
        } while (r < 2n || r >= N);

        // 4. Blind: m' = m * r^e mod N
        const blinded = (m * modPow(r, e, N)) % N;

        // 5. Request blind signature
        const bsRes = await fetch(`/issues/${issueID}/blind-sign`, {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({ blinded_vote: Array.from(atob(bigIntToBase64(blinded))).map(c => c.charCodeAt(0)) }),
        });
        if (!bsRes.ok) {
            const err = await bsRes.json();
            throw new Error(err.error?.message ?? "Blind-Sign fehlgeschlagen.");
        }
        const { signed } = await bsRes.json();
        const sPrime = base64ToBigInt(signed);

        // 6. Unblind: s = s' * r^(-1) mod N
        const signature = (sPrime * modInverse(r, N)) % N;

        // 7. Cast vote
        const voteRes = await fetch("/votes", {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({
                issue_id:  issueID,
                option_id: optionID,
                nonce:     bytesToBase64(nonce),
                signature: bigIntToBase64(signature),
            }),
        });
        if (!voteRes.ok) {
            const err = await voteRes.json();
            throw new Error(err.error?.message ?? "Abstimmung fehlgeschlagen.");
        }

        status.style.color = "green";
        status.textContent = "Stimme erfolgreich abgegeben!";
        setTimeout(() => window.location.reload(), 1500);
    } catch (err) {
        status.style.color = "red";
        status.textContent = err.message;
    }
}
</script>
{{end}}
