import { z } from 'zod';
import { resolveSecrets, type SecretMapping } from '@bytelyst/config';
import { registerTool } from '../tools/registry.js';

const COMMON_MAPPINGS: SecretMapping[] = [
  { kvName: 'bytelyst-cosmos-key', envVar: 'COSMOS_KEY' },
  { kvName: 'bytelyst-cosmos-endpoint', envVar: 'COSMOS_ENDPOINT' },
  { kvName: 'bytelyst-jwt-secret', envVar: 'JWT_SECRET' },
  { kvName: 'bytelyst-stripe-secret-key', envVar: 'STRIPE_SECRET_KEY' },
  { kvName: 'bytelyst-stripe-webhook-secret', envVar: 'STRIPE_WEBHOOK_SECRET' },
];

const PRODUCT_MAPPINGS: Record<string, SecretMapping[]> = {
  lysnrai: [
    { kvName: 'lysnr-stripe-secret-key', envVar: 'STRIPE_SECRET_KEY' },
    { kvName: 'lysnr-stripe-webhook-secret', envVar: 'STRIPE_WEBHOOK_SECRET' },
    { kvName: 'lysnr-billing-internal-key', envVar: 'BILLING_INTERNAL_KEY' },
    { kvName: 'lysnr-blob-connection-string', envVar: 'AZURE_BLOB_CONNECTION_STRING' },
    { kvName: 'lysnr-blob-account-key', envVar: 'AZURE_BLOB_ACCOUNT_KEY' },
  ],
  chronomind: [
    { kvName: 'chronomind-stripe-secret-key', envVar: 'STRIPE_SECRET_KEY' },
    { kvName: 'chronomind-stripe-webhook-secret', envVar: 'STRIPE_WEBHOOK_SECRET' },
  ],
  jarvisjr: [
    { kvName: 'jarvis-stripe-secret-key', envVar: 'STRIPE_SECRET_KEY' },
    { kvName: 'jarvis-stripe-webhook-secret', envVar: 'STRIPE_WEBHOOK_SECRET' },
  ],
  nomgap: [
    { kvName: 'nomgap-stripe-secret-key', envVar: 'STRIPE_SECRET_KEY' },
    { kvName: 'nomgap-stripe-webhook-secret', envVar: 'STRIPE_WEBHOOK_SECRET' },
  ],
  peakpulse: [
    { kvName: 'peakpulse-stripe-secret-key', envVar: 'STRIPE_SECRET_KEY' },
    { kvName: 'peakpulse-stripe-webhook-secret', envVar: 'STRIPE_WEBHOOK_SECRET' },
  ],
  mindlyst: [
    { kvName: 'mindlyst-stripe-secret-key', envVar: 'STRIPE_SECRET_KEY' },
    { kvName: 'mindlyst-stripe-webhook-secret', envVar: 'STRIPE_WEBHOOK_SECRET' },
  ],
};

function getMappings(productId?: string): SecretMapping[] {
  if (productId && PRODUCT_MAPPINGS[productId]) {
    return [...COMMON_MAPPINGS, ...PRODUCT_MAPPINGS[productId]];
  }

  return [...COMMON_MAPPINGS];
}

registerTool({
  name: 'secrets.listMappings',
  description:
    'List current secret mappings (which env vars map to which Key Vault secrets). Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    productId: z
      .string()
      .optional()
      .describe('Optional product filter to show product-specific mappings'),
  }),
  async execute(args, _req) {
    const { productId } = args;
    const mappings = getMappings(productId);

    // Show current status
    const status = mappings.map(mapping => ({
      kvName: mapping.kvName,
      envVar: mapping.envVar,
      isSet: !!process.env[mapping.envVar],
      isEmpty: !process.env[mapping.envVar],
    }));

    return {
      productId: productId || 'all',
      totalMappings: mappings.length,
      mappings: status,
      summary: `${status.filter(s => s.isSet).length}/${status.length} secrets are currently set in environment`,
    };
  },
});

registerTool({
  name: 'secrets.rotate',
  description:
    'Rotate a specific secret by fetching fresh value from Key Vault and updating environment. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    kvName: z.string().min(1).describe('Key Vault secret name to rotate'),
    envVar: z.string().min(1).describe('Environment variable to update'),
    productId: z.string().optional().describe('Optional product context for logging'),
  }),
  async execute(args, req) {
    const { kvName, envVar, productId } = args;
    const runId = `secret-rotate-${Date.now()}`;

    req.log.info({ runId, kvName, envVar, productId }, 'Starting secret rotation');

    try {
      // Store original value for rollback
      const originalValue = process.env[envVar];

      // Clear the env var to force refresh from Key Vault
      delete process.env[envVar];

      // Resolve fresh value from Key Vault
      await resolveSecrets([{ kvName, envVar }]);

      const newValue = process.env[envVar];

      if (!newValue) {
        // Rollback on failure
        if (originalValue) {
          process.env[envVar] = originalValue;
        }
        throw new Error(`Failed to fetch secret ${kvName} from Key Vault`);
      }

      const changed = originalValue !== newValue;

      req.log.info(
        {
          runId,
          kvName,
          envVar,
          productId,
          changed,
          wasPreviouslySet: !!originalValue,
          valueLength: newValue.length,
        },
        changed ? 'Secret successfully rotated' : 'Secret value unchanged'
      );

      return {
        runId,
        kvName,
        envVar,
        productId,
        changed,
        wasPreviouslySet: !!originalValue,
        valueLength: newValue.length,
        summary: changed
          ? `Successfully rotated ${kvName} → ${envVar}`
          : `Secret ${kvName} value unchanged`,
      };
    } catch (error) {
      req.log.error(
        {
          runId,
          kvName,
          envVar,
          productId,
          error: error instanceof Error ? error.message : String(error),
        },
        'Secret rotation failed'
      );

      throw error;
    }
  },
});

registerTool({
  name: 'secrets.validateMappings',
  description:
    'Validate that all required secret mappings can be resolved from Key Vault. Requires admin role.',
  requiredRole: 'admin',
  inputSchema: z.object({
    productId: z.string().optional().describe('Optional product filter'),
    dryRun: z
      .boolean()
      .optional()
      .default(false)
      .describe('If true, only validate without updating environment'),
  }),
  async execute(args, req) {
    const { productId, dryRun } = args;
    const runId = `secret-validate-${Date.now()}`;

    req.log.info({ runId, productId, dryRun }, 'Starting secret mapping validation');

    try {
      const mappings = getMappings(productId);

      const results = [];
      let successCount = 0;
      let failureCount = 0;

      for (const mapping of mappings) {
        const originalValue = process.env[mapping.envVar];

        try {
          if (!dryRun) {
            // Clear and resolve
            delete process.env[mapping.envVar];
            await resolveSecrets([mapping]);
          }

          const resolvedValue = process.env[mapping.envVar];

          if (resolvedValue) {
            successCount++;
            results.push({
              kvName: mapping.kvName,
              envVar: mapping.envVar,
              status: 'success',
              valueLength: resolvedValue.length,
              wasPreviouslySet: !!originalValue,
              changed: !dryRun && originalValue !== resolvedValue,
            });
          } else {
            failureCount++;
            results.push({
              kvName: mapping.kvName,
              envVar: mapping.envVar,
              status: 'failed',
              error: 'Secret not found in Key Vault or empty',
              wasPreviouslySet: !!originalValue,
            });

            // Restore original value on failure
            if (originalValue && !dryRun) {
              process.env[mapping.envVar] = originalValue;
            }
          }
        } catch (error) {
          failureCount++;
          results.push({
            kvName: mapping.kvName,
            envVar: mapping.envVar,
            status: 'error',
            error: error instanceof Error ? error.message : String(error),
            wasPreviouslySet: !!originalValue,
          });

          // Restore original value on error
          if (originalValue && !dryRun) {
            process.env[mapping.envVar] = originalValue;
          }
        }
      }

      const summary = dryRun
        ? `[DRY RUN] Validation complete: ${successCount}/${mappings.length} secrets resolvable`
        : `Validation complete: ${successCount}/${mappings.length} secrets validated`;

      req.log.info(
        { runId, productId, dryRun, successCount, failureCount, total: mappings.length },
        'Secret validation completed'
      );

      return {
        runId,
        productId,
        dryRun,
        totalMappings: mappings.length,
        successCount,
        failureCount,
        results,
        summary,
      };
    } catch (error) {
      req.log.error(
        { runId, productId, dryRun, error: error instanceof Error ? error.message : String(error) },
        'Secret validation failed'
      );

      throw error;
    }
  },
});
