import { NextRequest, NextResponse } from 'next/server';
import { createClient } from 'redis';
import { requireAdmin } from '@/lib/auth-server';

export const dynamic = 'force-dynamic';

interface ValkeyPreview {
  key: string;
  type: string;
  ttlSeconds: number;
  size?: number;
  preview?: string;
}

function sanitizePattern(input: string | null): string {
  const value = (input || '*').trim();
  if (!value) return '*';
  if (value.length > 120 || /[\r\n]/.test(value)) {
    throw new Error('Invalid key pattern');
  }
  return value;
}

function truncate(value: string, max = 160): string {
  return value.length > max ? `${value.slice(0, max)}...` : value;
}

function parseInfoValue(info: string, key: string): string | undefined {
  const line = info
    .split('\n')
    .map(item => item.trim())
    .find(item => item.startsWith(`${key}:`));

  return line?.split(':').slice(1).join(':');
}

async function getPreview(
  client: ReturnType<typeof createClient>,
  key: string
): Promise<ValkeyPreview> {
  const [type, ttlSeconds] = await Promise.all([client.type(key), client.ttl(key)]);

  if (type === 'string') {
    const value = await client.get(key);
    return { key, type, ttlSeconds, preview: truncate(value ?? '') };
  }

  if (type === 'hash') {
    const [size, entries] = await Promise.all([client.hLen(key), client.hGetAll(key)]);
    return {
      key,
      type,
      ttlSeconds,
      size,
      preview: truncate(JSON.stringify(Object.fromEntries(Object.entries(entries).slice(0, 5)))),
    };
  }

  if (type === 'list') {
    const [size, entries] = await Promise.all([client.lLen(key), client.lRange(key, 0, 4)]);
    return {
      key,
      type,
      ttlSeconds,
      size,
      preview: truncate(JSON.stringify(entries)),
    };
  }

  if (type === 'set') {
    const [size, entries] = await Promise.all([client.sCard(key), client.sMembers(key)]);
    return {
      key,
      type,
      ttlSeconds,
      size,
      preview: truncate(JSON.stringify(entries.slice(0, 5))),
    };
  }

  if (type === 'zset') {
    const [size, entries] = await Promise.all([
      client.zCard(key),
      client.zRangeWithScores(key, 0, 4),
    ]);
    return {
      key,
      type,
      ttlSeconds,
      size,
      preview: truncate(JSON.stringify(entries)),
    };
  }

  return { key, type, ttlSeconds };
}

export async function GET(req: NextRequest) {
  try {
    const admin = await requireAdmin(req);
    if (!admin) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

    const url = new URL(req.url);
    const pattern = sanitizePattern(url.searchParams.get('pattern'));
    const limit = Math.min(Math.max(Number(url.searchParams.get('limit') || '25'), 1), 100);
    const client = createClient({
      url: process.env.VALKEY_URL || 'redis://valkey:6379',
    });

    try {
      await client.connect();
      const [ping, dbsize, serverInfo, memoryInfo] = await Promise.all([
        client.ping(),
        client.dbSize(),
        client.info('server'),
        client.info('memory'),
      ]);

      const keys: string[] = [];
      for await (const key of client.scanIterator({
        MATCH: pattern,
        COUNT: Math.min(limit * 2, 200),
      })) {
        keys.push(key);
        if (keys.length >= limit) break;
      }

      const previews = await Promise.all(keys.map(key => getPreview(client, key)));

      return NextResponse.json({
        timestamp: new Date().toISOString(),
        pattern,
        limit,
        summary: {
          ping,
          dbsize,
          matchedKeys: previews.length,
          version: parseInfoValue(serverInfo, 'redis_version') || 'unknown',
          usedMemoryHuman: parseInfoValue(memoryInfo, 'used_memory_human') || 'unknown',
          usedMemoryPeakHuman: parseInfoValue(memoryInfo, 'used_memory_peak_human') || 'unknown',
        },
        keys: previews,
      });
    } finally {
      if (client.isOpen) {
        await client.quit();
      }
    }
  } catch (error) {
    const message = error instanceof Error ? error.message : 'Unable to inspect Valkey';
    return NextResponse.json(
      { error: message },
      { status: message === 'Unauthorized' ? 401 : 500 }
    );
  }
}

export async function POST(req: NextRequest) {
  try {
    const admin = await requireAdmin(req);
    if (!admin) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });

    const body = await req.json().catch(() => null);
    if (!body || typeof body.action !== 'string') {
      return NextResponse.json({ error: 'Invalid request body' }, { status: 400 });
    }

    const client = createClient({
      url: process.env.VALKEY_URL || 'redis://valkey:6379',
    });

    try {
      await client.connect();

      if (body.action === 'deleteKey') {
        if (typeof body.key !== 'string' || !body.key.trim()) {
          return NextResponse.json({ error: 'key is required' }, { status: 400 });
        }

        const deleted = await client.del(body.key.trim());
        return NextResponse.json({
          ok: true,
          action: 'deleteKey',
          key: body.key.trim(),
          deleted,
        });
      }

      if (body.action === 'deletePattern') {
        const pattern = sanitizePattern(typeof body.pattern === 'string' ? body.pattern : null);
        if (pattern === '*' || pattern.startsWith('*')) {
          return NextResponse.json(
            { error: 'Pattern delete requires a concrete prefix and cannot start with *' },
            { status: 400 }
          );
        }

        const limit = Math.min(Math.max(Number(body.limit || 25), 1), 100);
        const keys: string[] = [];
        for await (const key of client.scanIterator({
          MATCH: pattern,
          COUNT: Math.min(limit * 2, 200),
        })) {
          keys.push(key);
          if (keys.length >= limit) break;
        }

        const deleted = keys.length > 0 ? await client.del(keys) : 0;
        return NextResponse.json({
          ok: true,
          action: 'deletePattern',
          pattern,
          matched: keys.length,
          deleted,
          keys,
        });
      }

      return NextResponse.json({ error: 'Unsupported action' }, { status: 400 });
    } finally {
      if (client.isOpen) {
        await client.quit();
      }
    }
  } catch (error) {
    const message = error instanceof Error ? error.message : 'Valkey write failed';
    return NextResponse.json(
      { error: message },
      { status: message === 'Unauthorized' ? 401 : 500 }
    );
  }
}
