import { createClient } from 'redis';
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';

const redis = createClient({ url: process.env.REDIS_URL });
redis.connect().catch((err) => console.error('[billing] Redis connect error:', err));

export interface Customer {
  id: string;
  plan: PlanKey;
  active: boolean;
  email: 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;
}

async function resolveCustomer(apiKey: string): Promise<Customer | null> {
  const cached = await redis.get(`customer:apikey:${apiKey}`);
  if (cached) {
    const base = JSON.parse(cached) as Omit<Customer, 'getCredential'>;
    // Re-attach the credential loader (functions can't be cached)
    return {
      ...base,
      getCredential: <T extends PlatformCredentials>(platform: Platform) =>
        getCredential<T>(base.id, platform),
    };
  }

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

  const { id, plan, active, email } = rows[0];
  const customer: Customer = {
    id,
    plan,
    active: Boolean(active),
    email,
    getCredential: <T extends PlatformCredentials>(platform: Platform) =>
      getCredential<T>(id, platform),
  };

  // Cache only the serialisable fields (not the function)
  await redis.setEx(`customer:apikey:${apiKey}`, 60, JSON.stringify({ id, plan, active, email }));
  return customer;
}

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

    if (!apiKey) {
      res.status(401).json({ error: 'Missing API key' });
      return;
    }

    const customer = await resolveCustomer(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();
  } catch (err) {
    next(err);
  }
}
