from __future__ import annotations

import json
import secrets
import sys
import time
from http.server import BaseHTTPRequestHandler, HTTPServer
from pathlib import Path

ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
    sys.path.insert(0, str(ROOT))

from configs.kling import KlingConfig
from scripts.db import init_db
from scripts.repository import create_asset, get_task, update_task_from_callback

CALLBACK_DIR = ROOT / 'logs' / 'callbacks'
CALLBACK_DIR.mkdir(parents=True, exist_ok=True)


def _auth_mode(headers) -> str | None:
    cfg = KlingConfig()
    if cfg.callback_permissive_debug:
        return 'permissive_debug'
    secret = cfg.callback_shared_secret
    if not secret:
        return None
    provided = headers.get('X-Callback-Secret') or headers.get('Authorization')
    if provided and provided.startswith('Bearer '):
        provided = provided[len('Bearer '):]
    if bool(provided) and secrets.compare_digest(provided, secret):
        return 'shared_secret'
    return None


def _extract_assets(task_id_internal: str | None, payload: dict):
    if not task_id_internal:
        return
    task = get_task(task_id_internal)
    scene_id = task['scene_id'] if task else None
    result = payload.get('task_result') or {}
    for video in result.get('videos', []) or []:
        create_asset(scene_id, task_id_internal, 'video', video.get('url'), video.get('watermark_url'), None, video.get('duration'), video)
    for image in result.get('images', []) or []:
        create_asset(scene_id, task_id_internal, 'image', image.get('url'), None, None, None, image)


class CallbackHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        auth_mode = _auth_mode(self.headers)
        if not auth_mode:
            self.send_response(401)
            self.send_header('Content-Type', 'application/json')
            self.end_headers()
            self.wfile.write(b'{"ok":false,"error":"unauthorized"}')
            return
        length = int(self.headers.get('Content-Length', '0'))
        raw = self.rfile.read(length)
        try:
            payload = json.loads(raw.decode('utf-8'))
        except Exception:
            payload = {'_raw': raw.decode('utf-8', errors='ignore')}
        task_id = payload.get('task_id', 'unknown')
        out = CALLBACK_DIR / f'{int(time.time()*1000)}_{task_id}.json'
        out.write_text(json.dumps(payload, ensure_ascii=False, indent=2))
        task_id_internal = update_task_from_callback(payload, auth_mode=auth_mode)
        _extract_assets(task_id_internal, payload)
        self.send_response(200)
        self.send_header('Content-Type', 'application/json')
        self.end_headers()
        self.wfile.write(b'{"ok":true}')


def run(host: str = '0.0.0.0', port: int = 8787):
    init_db()
    server = HTTPServer((host, port), CallbackHandler)
    print(f'callback receiver listening on http://{host}:{port}')
    server.serve_forever()


if __name__ == '__main__':
    run()
