import { RowDataPacket } from 'mysql2';
import { getPool } from '../db.js';
import { getCredential, Platform, PlatformCredentials } from '../multitenancy/credential-store.js';
import type { PlanKey } from './plans.js';
import type { Request, Response, NextFunction } from 'express';
import { verifyJWT } from '../auth.js';
import redis from '../redis.js';

export interface Customer {
  id: string;
  plan: PlanKey;
  active: boolean;
  email: string;
  role: string;
  // Credential loader — tool handlers call this to get their platform credentials
  getCredential: <T extends PlatformCredentials>(platform: Platform) => Promise<T | null>;
}

interface CustomerRow extends RowDataPacket {
  id: string;
  plan: PlanKey;
  active: boolean;
  email: string;
  role: string;
}

function buildCustomer(row: CustomerRow): Customer {
  return {
    id: row.id,
    plan: row.plan,
    active: Boolean(row.active),
    email: row.email,
    role: row.role || 'user',
    getCredential: <T extends PlatformCredentials>(platform: Platform) =>
      getCredential<T>(row.id, platform),
  };
}

export async function resolveCustomerByApiKey(apiKey: string): Promise<Customer | null> {
  const cached = await redis.get(`customer:apikey:${apiKey}`);
  if (cached) {
    const base = JSON.parse(cached) as Omit<CustomerRow, 'getCredential'>;
    return buildCustomer(base as CustomerRow);
  }

  const [rows] = await getPool().query<CustomerRow[]>(
    'SELECT id, plan, active, email FROM customers WHERE api_key = ?',
    [apiKey]
  );
  if (!rows.length) return null;

  const row = rows[0];
  await redis.setEx(`customer:apikey:${apiKey}`, 60, JSON.stringify(row));
  return buildCustomer(row);
}

export async function resolveCustomerById(id: string): Promise<Customer | null> {
  const cached = await redis.get(`customer:id:${id}`);
  if (cached) {
    const base = JSON.parse(cached) as CustomerRow;
    return buildCustomer(base);
  }

  const [rows] = await getPool().query<CustomerRow[]>(
    'SELECT id, plan, active, email FROM customers WHERE id = ?',
    [id]
  );
  if (!rows.length) return null;

  const row = rows[0];
  await redis.setEx(`customer:id:${id}`, 60, JSON.stringify(row));
  return buildCustomer(row);
}

// Express middleware: resolve API key OR JWT cookie → Customer and attach to req.customer
export async function meterMiddleware(
  req: Request,
  res: Response,
  next: NextFunction
): Promise<void> {
  try {
    // 1. Try API key
    const apiKey =
      (req.headers['x-api-key'] as string | undefined) ||
      (req.query.key as string | undefined);

    if (apiKey) {
      const customer = await resolveCustomerByApiKey(apiKey);
      if (!customer) {
        res.status(401).json({ error: 'Invalid API key' });
        return;
      }
      if (!customer.active) {
        res.status(403).json({ error: 'Account suspended' });
        return;
      }
      (req as Request & { customer: Customer }).customer = customer;
      next();
      return;
    }

    // 2. Try JWT session cookie
    const jwtCookie = req.cookies?.session;
    if (jwtCookie) {
      try {
        const payload = verifyJWT(jwtCookie);
        const customer = await resolveCustomerById(payload.sub);
        if (!customer) {
          res.status(401).json({ error: 'Invalid session' });
          return;
        }
        if (!customer.active) {
          res.status(403).json({ error: 'Account suspended' });
          return;
        }
        (req as Request & { customer: Customer }).customer = customer;
        next();
        return;
      } catch {
        res.status(401).json({ error: 'Invalid or expired session' });
        return;
      }
    }

    res.status(401).json({ error: 'Missing API key or session' });
  } catch (err) {
    next(err);
  }
}
