# retoor <retoor@molodetz.nl>

from sqlalchemy import text

from devplacepy.database import CONSENTS_TABLE, _now_iso, db, get_table

from .agreements import Agreement
from .pending import has_gate_column, not_withdrawn, satisfied_clause

CONSENT_COLUMNS = (
    "uid",
    "owner_kind",
    "owner_id",
    "kind",
    "version",
    "state",
    "granted_at",
    "withdrawn_at",
    "created_at",
    "deleted_at",
    "deleted_by",
)


def _gate_set_clause(agreement: Agreement) -> str:
    users = get_table("users")
    assignments = [f"{agreement.gate_column} = :version"]
    if agreement.stamp_column and users.has_column(agreement.stamp_column):
        assignments.append(f"{agreement.stamp_column} = :now")
    return ", ".join(assignments)


def _insert_consent_sql(precondition: str) -> str:
    columns = ", ".join(CONSENT_COLUMNS)
    sql = (
        f"INSERT INTO {CONSENTS_TABLE} ({columns}) "
        "SELECT :row_uid, 'user', :uid, :kind, :version, 'granted', "
        ":now, '', :now, NULL, NULL"
    )
    if precondition:
        return f"{sql} WHERE {precondition}"
    return sql


def _consent_params(agreement: Agreement, uid: str, version: str, now: str) -> dict:
    from devplacepy.utils import generate_uid

    return {
        "row_uid": generate_uid(),
        "uid": uid,
        "kind": agreement.kind,
        "version": version,
        "now": now,
    }


def _claim_gate_column(agreement: Agreement, uid: str, version: str, now: str) -> bool:
    claim = (
        f"UPDATE users SET {_gate_set_clause(agreement)} "
        "WHERE uid = :uid "
        f"AND COALESCE({agreement.gate_column}, '') != :version "
        f"AND {not_withdrawn('users.uid')}"
    )
    params = _consent_params(agreement, uid, version, now)
    with db:
        claimed = db.executable.execute(text(claim), params).rowcount
        if claimed != 1:
            return False
        db.executable.execute(text(_insert_consent_sql("")), params)
    return True


def _claim_ledger(agreement: Agreement, uid: str, version: str, now: str) -> bool:
    precondition = (
        f"{not_withdrawn(':uid')} AND {satisfied_clause(agreement, ':uid', '')}"
    )
    params = _consent_params(agreement, uid, version, now)
    with db:
        written = db.executable.execute(
            text(_insert_consent_sql(precondition)), params
        ).rowcount
    return written == 1


def _record(agreement: Agreement, user: dict, version: str) -> None:
    from devplacepy.services.audit import record as audit

    uid = user["uid"]
    username = user.get("username") or ""
    links = [audit.target("user", uid, username)]
    if agreement.kind == "terms":
        audit.record_system(
            "terms.accept",
            actor_kind="service",
            actor_username="acceptance",
            target_type="user",
            target_uid=uid,
            target_label=username,
            new_value=version,
            summary=f"{username} accepted terms version {version}",
            links=links,
        )
        return
    audit.record_system(
        "consent.grant",
        actor_kind="service",
        actor_username="acceptance",
        target_type="user",
        target_uid=uid,
        target_label=username,
        new_value="granted",
        metadata={"kind": agreement.kind},
        summary=f"granted {agreement.kind} consent for {username}",
        links=links,
    )


def converge_user(agreement: Agreement, user: dict, version: str) -> bool:
    uid = user.get("uid")
    if not uid:
        return False
    now = _now_iso()
    if has_gate_column(agreement):
        won = _claim_gate_column(agreement, uid, version, now)
    else:
        won = _claim_ledger(agreement, uid, version, now)
    if not won:
        return False
    _record(agreement, user, version)
    return True
