#!/usr/bin/env python3
"""
BlackTeam — VIN ruxsat serveri (port 8091).
Ovozli lumen-backend (8090) dan ALOHIDA.

Modul: POST /api/vin/verify  +  /api/vin/data
"""
from __future__ import annotations

import hashlib
import hmac
import json
import os
import sqlite3
import time
from pathlib import Path

from flask import Flask, Response, request

APP = Flask(__name__)
DB = Path(os.environ.get("ACTIVATION_DB", Path(__file__).with_name("vins.db")))
API_KEY = os.environ.get("ACTIVATION_API_KEY", "spm8666p1_64_car")
# com.module.SecureApi X-Api-Key (mashina shu kalitni yuboradi)
MODULE_API_KEY = "9BaJTNAU2WjkbgSbIxgj6gH5uh5rjTb9"
ALLOWED_API_KEYS = {API_KEY, MODULE_API_KEY}
API_SECRET = os.environ.get("ACTIVATION_API_SECRET", API_KEY)
ADMIN_PASS = os.environ.get("ACTIVATION_ADMIN_PASS", "BlackTeam2026")
STRICT_HMAC = os.environ.get("ACTIVATION_STRICT_HMAC", "0") == "1"


def db() -> sqlite3.Connection:
    con = sqlite3.connect(DB)
    con.row_factory = sqlite3.Row
    return con


def init_db() -> None:
    with db() as con:
        con.execute(
            """
            CREATE TABLE IF NOT EXISTS vins (
                vin TEXT PRIMARY KEY,
                status TEXT NOT NULL DEFAULT 'ACTIVE',
                note TEXT DEFAULT '',
                updated_at INTEGER NOT NULL
            )
            """
        )
        con.commit()


def vin_status(vin: str) -> str:
    vin = (vin or "").strip().upper()
    if not vin:
        return "INACTIVE"
    with db() as con:
        row = con.execute("SELECT status FROM vins WHERE vin=?", (vin,)).fetchone()
    if not row:
        return "INACTIVE"
    return row["status"].upper()


def api_key_ok() -> bool:
    key = request.headers.get("X-Api-Key")
    if not key:
        return True
    return key in ALLOWED_API_KEYS


def check_hmac() -> bool:
    if not STRICT_HMAC:
        return True
    ts = request.headers.get("X-Timestamp", "")
    sig = request.headers.get("X-Signature", "")
    key = request.headers.get("X-Api-Key", "")
    if key not in ALLOWED_API_KEYS or not ts or not sig:
        return False
    body = request.get_data() or b""
    for msg in (ts.encode() + body, (ts + body.decode("utf-8", errors="ignore")).encode()):
        for secret in (API_SECRET.encode(), API_KEY.encode()):
            exp = hmac.new(secret, msg, hashlib.sha256).hexdigest()
            if hmac.compare_digest(exp, sig.lower()):
                return True
            exp2 = hmac.new(secret, msg, hashlib.sha256).digest()
            if hmac.compare_digest(exp2.hex(), sig.lower()):
                return True
    return False


def extract_vin() -> str:
    if request.is_json:
        data = request.get_json(silent=True) or {}
        if isinstance(data, dict):
            for k in ("vin", "VIN", "Vin"):
                if data.get(k):
                    return str(data[k])
    raw = request.get_data(as_text=True) or ""
    if raw.strip().startswith("{"):
        try:
            data = json.loads(raw)
            if isinstance(data, dict) and data.get("vin"):
                return str(data["vin"])
        except json.JSONDecodeError:
            pass
    return (request.args.get("vin") or request.form.get("vin") or "").strip()


@APP.before_request
def _log():
    if request.path.startswith("/api/vin"):
        print(
            f"[vin] {request.method} {request.path} "
            f"key={request.headers.get('X-Api-Key')} "
            f"body={request.get_data(as_text=True)[:200]}"
        )


@APP.route("/api/vin/data", methods=["GET", "POST", "OPTIONS"])
def api_vin_data():
    if request.method == "OPTIONS":
        return Response(status=204)
    if not api_key_ok():
        return {"code": 403, "message": "Forbidden", "data": {}}, 403
    if not check_hmac():
        return {"code": 401, "message": "Bad signature", "data": {}}, 401
    vin = extract_vin()
    st = vin_status(vin).lower()
    return {
        "code": 0,
        "message": "OK",
        "data": {"vin": vin, "status": st},
    }


@APP.route("/api/vin/verify", methods=["GET", "POST", "OPTIONS"])
def api_vin_verify():
    if request.method == "OPTIONS":
        return Response(status=204)
    if not api_key_ok():
        return {"code": 403, "message": "Forbidden", "data": {}}, 403
    if not check_hmac():
        return {"code": 401, "message": "Bad signature", "data": {}}, 401
    vin = extract_vin()
    st = vin_status(vin).lower()
    # Modul JSON ichida "active" / "inactive" / "waiting" (kichik harf) qidiradi
    return {
        "code": 0,
        "message": "OK",
        "data": {"vin": vin, "status": st},
        "status": st,
    }


@APP.route("/activation-admin", methods=["GET", "POST"])
def admin():
    err = ""
    msg = ""
    if request.method == "POST":
        if request.form.get("password") != ADMIN_PASS:
            err = "Parol noto'g'ri"
        else:
            action = request.form.get("action")
            vin = (request.form.get("vin") or "").strip().upper()
            if action == "allow" and vin:
                with db() as con:
                    con.execute(
                        "INSERT OR REPLACE INTO vins(vin,status,note,updated_at) VALUES(?,?,?,?)",
                        (vin, "ACTIVE", request.form.get("note") or "", int(time.time())),
                    )
                    con.commit()
                msg = f"{vin} — ACTIVE (ruxsat berildi)"
            elif action == "deny" and vin:
                with db() as con:
                    con.execute(
                        "INSERT OR REPLACE INTO vins(vin,status,note,updated_at) VALUES(?,?,?,?)",
                        (vin, "INACTIVE", request.form.get("note") or "", int(time.time())),
                    )
                    con.commit()
                msg = f"{vin} — INACTIVE (rad)"
            elif action == "delete" and vin:
                with db() as con:
                    con.execute("DELETE FROM vins WHERE vin=?", (vin,))
                    con.commit()
                msg = f"{vin} o'chirildi"
    rows = []
    with db() as con:
        rows = con.execute(
            "SELECT vin, status, note, updated_at FROM vins ORDER BY updated_at DESC"
        ).fetchall()
    rows_html = "".join(
        f"<tr><td>{r['vin']}</td><td>{r['status']}</td><td>{r['note'] or ''}</td></tr>"
        for r in rows
    ) or "<tr><td colspan=3>Ro'yxat bo'sh</td></tr>"
    msg_html = f"<p class='ok'>{msg}</p>" if msg else ""
    err_html = f"<p class='er'>{err}</p>" if err else ""
    return f"""<!DOCTYPE html>
<html><head><meta charset='utf-8'><title>VIN ruxsat</title>
<style>body{{font-family:sans-serif;max-width:720px;margin:2rem auto}}
table{{border-collapse:collapse;width:100%}}td,th{{border:1px solid #ccc;padding:8px}}
.ok{{color:green}}.er{{color:red}}</style></head>
<body>
<h1>BlackTeam — VIN ruxsat</h1>
<p>Mijoz Telegramdan VIN yuboradi — <b>Ruxsat berish</b> bosing.</p>
{msg_html}
{err_html}
<form method='post'>
<label>Admin parol: <input name='password' type='password'></label><br><br>
<label>VIN: <input name='vin' size='24' placeholder='LDP45G965SD166717'></label><br><br>
<label>Izoh: <input name='note' size='40'></label><br><br>
<button name='action' value='allow'>Ruxsat berish (ACTIVE)</button>
<button name='action' value='deny'>Rad etish (INACTIVE)</button>
<button name='action' value='delete'>O&apos;chirish</button>
</form>
<h2>Ro'yxat</h2>
<table><tr><th>VIN</th><th>Status</th><th>Izoh</th></tr>{rows_html}</table>
</body></html>"""


@APP.route("/health")
def health():
    return {"ok": True, "service": "activation", "port": 8091}


if __name__ == "__main__":
    init_db()
    host = os.environ.get("ACTIVATION_HOST", "127.0.0.1")
    port = int(os.environ.get("ACTIVATION_PORT", "8091"))
    print(f"Activation API http://{host}:{port}")
    print(f"Admin http://{host}:{port}/activation-admin  parol: {ADMIN_PASS}")
    APP.run(host=host, port=port, debug=False)
