import session from 'express-session';
import { db } from './db.js';

export class SQLiteSessionStore extends session.Store {
  private readonly defaultTtlMs = 7 * 24 * 60 * 60 * 1000;

  constructor() {
    super();
    const timer = setInterval(() => this.cleanup(), 60 * 60 * 1000);
    timer.unref();
  }

  get(sid: string, callback: (err: unknown, session?: session.SessionData | null) => void): void {
    try {
      const row = db.prepare('SELECT data, expires_at AS expiresAt FROM web_sessions WHERE sid = ?').get(sid) as
        | { data: string; expiresAt: number }
        | undefined;
      if (!row || row.expiresAt <= Date.now()) {
        if (row) db.prepare('DELETE FROM web_sessions WHERE sid = ?').run(sid);
        callback(null, null);
        return;
      }
      callback(null, JSON.parse(row.data) as session.SessionData);
    } catch (error) {
      callback(error);
    }
  }

  set(sid: string, value: session.SessionData, callback?: (err?: unknown) => void): void {
    try {
      const expiresAt = value.cookie?.expires?.getTime() ?? Date.now() + this.defaultTtlMs;
      db.prepare(`
        INSERT INTO web_sessions (sid, expires_at, data) VALUES (?, ?, ?)
        ON CONFLICT(sid) DO UPDATE SET expires_at = excluded.expires_at, data = excluded.data
      `).run(sid, expiresAt, JSON.stringify(value));
      callback?.();
    } catch (error) {
      callback?.(error);
    }
  }

  destroy(sid: string, callback?: (err?: unknown) => void): void {
    try {
      db.prepare('DELETE FROM web_sessions WHERE sid = ?').run(sid);
      callback?.();
    } catch (error) {
      callback?.(error);
    }
  }

  touch(sid: string, value: session.SessionData, callback?: (err?: unknown) => void): void {
    this.set(sid, value, callback);
  }

  private cleanup(): void {
    db.prepare('DELETE FROM web_sessions WHERE expires_at <= ?').run(Date.now());
  }
}
