import * as plugins from '../../plugins.ts';
import type { OpsServer } from '../classes.opsserver.ts';
import * as interfaces from '../../../ts_interfaces/index.ts';
import { requireValidIdentity } from '../helpers/guards.ts';

export class SecretsHandler {
  public typedrouter = new plugins.typedrequest.TypedRouter();

  constructor(private opsServerRef: OpsServer) {
    this.opsServerRef.typedrouter.addTypedRouter(this.typedrouter);
    this.registerHandlers();
  }

  private get actionLog() {
    return this.opsServerRef.gitopsAppRef.actionLog;
  }

  private registerHandlers(): void {
    // Get all secrets (cache-first, falls back to live fetch)
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetAllSecrets>(
        'getAllSecrets',
        async (dataArg) => {
          await requireValidIdentity(this.opsServerRef.adminHandler, dataArg);
          const scanService = this.opsServerRef.gitopsAppRef.secretsScanService;

          // Try cache first
          const hasCached = await scanService.hasCachedData(dataArg.connectionId, dataArg.scope);
          if (hasCached) {
            const secrets = await scanService.getCachedSecrets({
              connectionId: dataArg.connectionId,
              scope: dataArg.scope,
            });
            return { secrets };
          }

          // Cache miss: live fetch and save to cache
          const provider = this.opsServerRef.gitopsAppRef.connectionManager.getProvider(
            dataArg.connectionId,
          );

          const allSecrets: interfaces.data.ISecret[] = [];

          if (dataArg.scope === 'project') {
            const projects = await provider.getProjects();
            for (let i = 0; i < projects.length; i += 5) {
              const batch = projects.slice(i, i + 5);
              const results = await Promise.allSettled(
                batch.map(async (p) => {
                  const secrets = await provider.getProjectSecrets(p.id);
                  return secrets.map((s) => ({
                    ...s,
                    scopeName: p.fullPath || p.name,
                    scope: 'project' as const,
                    scopeId: p.id,
                    connectionId: dataArg.connectionId,
                  }));
                }),
              );
              for (const result of results) {
                if (result.status === 'fulfilled') {
                  allSecrets.push(...result.value);
                }
              }
            }
          } else {
            const groups = await provider.getGroups();
            for (let i = 0; i < groups.length; i += 5) {
              const batch = groups.slice(i, i + 5);
              const results = await Promise.allSettled(
                batch.map(async (g) => {
                  const secrets = await provider.getGroupSecrets(g.id);
                  return secrets.map((s) => ({
                    ...s,
                    scopeName: g.fullPath || g.name,
                    scope: 'group' as const,
                    scopeId: g.id,
                    connectionId: dataArg.connectionId,
                  }));
                }),
              );
              for (const result of results) {
                if (result.status === 'fulfilled') {
                  allSecrets.push(...result.value);
                }
              }
            }
          }

          // Save fetched secrets to cache (fire-and-forget)
          scanService.saveSecrets(allSecrets).catch(() => {});

          return { secrets: allSecrets };
        },
      ),
    );

    // Get secrets (cache-first for single entity)
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetSecrets>(
        'getSecrets',
        async (dataArg) => {
          await requireValidIdentity(this.opsServerRef.adminHandler, dataArg);
          const scanService = this.opsServerRef.gitopsAppRef.secretsScanService;

          // Try cache first
          const cached = await scanService.getCachedSecrets({
            connectionId: dataArg.connectionId,
            scope: dataArg.scope,
            scopeId: dataArg.scopeId,
          });
          if (cached.length > 0) {
            return { secrets: cached };
          }

          // Cache miss: live fetch
          const provider = this.opsServerRef.gitopsAppRef.connectionManager.getProvider(
            dataArg.connectionId,
          );
          const secrets = dataArg.scope === 'project'
            ? await provider.getProjectSecrets(dataArg.scopeId)
            : await provider.getGroupSecrets(dataArg.scopeId);

          // Save to cache (fire-and-forget)
          const fullSecrets = secrets.map((s) => ({
            ...s,
            scope: dataArg.scope,
            scopeId: dataArg.scopeId,
            connectionId: dataArg.connectionId,
          }));
          scanService.saveSecrets(fullSecrets).catch(() => {});

          return { secrets };
        },
      ),
    );

    // Create secret
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateSecret>(
        'createSecret',
        async (dataArg) => {
          await requireValidIdentity(this.opsServerRef.adminHandler, dataArg);
          const provider = this.opsServerRef.gitopsAppRef.connectionManager.getProvider(
            dataArg.connectionId,
          );
          const secret = dataArg.scope === 'project'
            ? await provider.createProjectSecret(dataArg.scopeId, dataArg.key, dataArg.value)
            : await provider.createGroupSecret(dataArg.scopeId, dataArg.key, dataArg.value);
          // Refresh cache for this entity
          const scanService = this.opsServerRef.gitopsAppRef.secretsScanService;
          scanService.scanEntity(dataArg.connectionId, dataArg.scope, dataArg.scopeId).catch(() => {});
          this.actionLog.append({
            actionType: 'create',
            entityType: 'secret',
            entityId: `${dataArg.scopeId}/${dataArg.key}`,
            entityName: dataArg.key,
            details: `Created ${dataArg.scope} secret "${dataArg.key}" in ${dataArg.scopeId}`,
            username: dataArg.identity.username,
          });
          return { secret };
        },
      ),
    );

    // Update secret
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_UpdateSecret>(
        'updateSecret',
        async (dataArg) => {
          await requireValidIdentity(this.opsServerRef.adminHandler, dataArg);
          const provider = this.opsServerRef.gitopsAppRef.connectionManager.getProvider(
            dataArg.connectionId,
          );
          const secret = dataArg.scope === 'project'
            ? await provider.updateProjectSecret(dataArg.scopeId, dataArg.key, dataArg.value)
            : await provider.updateGroupSecret(dataArg.scopeId, dataArg.key, dataArg.value);
          // Refresh cache for this entity
          const scanService = this.opsServerRef.gitopsAppRef.secretsScanService;
          scanService.scanEntity(dataArg.connectionId, dataArg.scope, dataArg.scopeId).catch(() => {});
          this.actionLog.append({
            actionType: 'update',
            entityType: 'secret',
            entityId: `${dataArg.scopeId}/${dataArg.key}`,
            entityName: dataArg.key,
            details: `Updated ${dataArg.scope} secret "${dataArg.key}" in ${dataArg.scopeId}`,
            username: dataArg.identity.username,
          });
          return { secret };
        },
      ),
    );

    // Delete secret
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteSecret>(
        'deleteSecret',
        async (dataArg) => {
          await requireValidIdentity(this.opsServerRef.adminHandler, dataArg);
          const provider = this.opsServerRef.gitopsAppRef.connectionManager.getProvider(
            dataArg.connectionId,
          );
          if (dataArg.scope === 'project') {
            await provider.deleteProjectSecret(dataArg.scopeId, dataArg.key);
          } else {
            await provider.deleteGroupSecret(dataArg.scopeId, dataArg.key);
          }
          // Refresh cache for this entity
          const scanService = this.opsServerRef.gitopsAppRef.secretsScanService;
          scanService.scanEntity(dataArg.connectionId, dataArg.scope, dataArg.scopeId).catch(() => {});
          this.actionLog.append({
            actionType: 'delete',
            entityType: 'secret',
            entityId: `${dataArg.scopeId}/${dataArg.key}`,
            entityName: dataArg.key,
            details: `Deleted ${dataArg.scope} secret "${dataArg.key}" from ${dataArg.scopeId}`,
            username: dataArg.identity.username,
          });
          return { ok: true };
        },
      ),
    );
  }
}
