from __future__ import annotations

import hashlib
import json
import threading

from sqlalchemy import select
from sqlalchemy.orm import Session

from ..models import ActivityLog, Computer, utcnow

_lock = threading.Lock()


def _hash(prev: str | None, row: ActivityLog) -> str:
    body = json.dumps(
        [prev or "", row.actor_type, row.actor_id, row.actor_name, row.action, row.computer_id, row.status,
         row.detail, row.reason, row.created_at.isoformat()], sort_keys=True, default=str, ensure_ascii=False)
    return hashlib.sha256(body.encode()).hexdigest()


def log(db: Session, actor_type: str, actor_name: str, action: str, *, actor_id: int | None = None,
        computer: Computer | int | None = None, status: str = "success", detail: dict | None = None,
        reason: str | None = None, ip: str | None = None) -> ActivityLog:
    cid, cname = None, None
    if isinstance(computer, Computer):
        cid, cname = computer.id, computer.label or computer.hostname
    elif isinstance(computer, int):
        c = db.get(Computer, computer)
        cid, cname = computer, (c.label or c.hostname) if c else None
    with _lock:
        prev = db.execute(select(ActivityLog.row_hash).order_by(ActivityLog.id.desc()).limit(1)).scalar()
        row = ActivityLog(actor_type=actor_type, actor_id=actor_id, actor_name=actor_name, action=action, computer_id=cid,
                          computer_name=cname, status=status, detail=detail, reason=reason, ip_address=ip,
                          created_at=utcnow(), prev_hash=prev)
        row.row_hash = _hash(prev, row)
        db.add(row)
        db.commit()
    return row


def verify_chain(db: Session) -> dict:
    prev, n = None, 0
    for row in db.execute(select(ActivityLog).order_by(ActivityLog.id)).scalars():
        if row.prev_hash != prev or row.row_hash != _hash(prev, row):
            return {"ok": False, "broken_at": row.id, "checked": n}
        prev, n = row.row_hash, n + 1
    return {"ok": True, "checked": n}
