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

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

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

  private registerHandlers(): void {
    // Get registry tags
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRegistryTags>(
        'getRegistryTags',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const tags = await this.opsServerRef.oneboxRef.registry.getImageTags(dataArg.serviceName);
          return { tags };
        },
      ),
    );

    // Get registry tokens
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_GetRegistryTokens>(
        'getRegistryTokens',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const rawTokens = await this.opsServerRef.oneboxRef.database.getAllRegistryTokens();
          const now = Date.now();

          const tokens = rawTokens.map((token: any) => {
            const isExpired = token.expiresAt !== null && token.expiresAt < now;
            let scopeDisplay: string;
            if (token.scope === 'all') {
              scopeDisplay = 'All services';
            } else if (Array.isArray(token.scope)) {
              scopeDisplay = token.scope.length === 1 ? token.scope[0] : `${token.scope.length} services`;
            } else {
              scopeDisplay = 'Unknown';
            }

            return {
              id: token.id!,
              name: token.name,
              type: token.type,
              scope: token.scope,
              scopeDisplay,
              expiresAt: token.expiresAt,
              createdAt: token.createdAt,
              lastUsedAt: token.lastUsedAt,
              createdBy: token.createdBy,
              isExpired,
            };
          });

          return { tokens };
        },
      ),
    );

    // Create registry token
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_CreateRegistryToken>(
        'createRegistryToken',
        async (dataArg) => {
          const identity = await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const config = dataArg.tokenConfig;

          // Calculate expiration
          const now = Date.now();
          let expiresAt: number | null = null;
          if (config.expiresIn !== 'never') {
            const daysMap: Record<string, number> = { '30d': 30, '90d': 90, '365d': 365 };
            const days = daysMap[config.expiresIn];
            if (days) expiresAt = now + days * 24 * 60 * 60 * 1000;
          }

          // Generate token
          const plainToken = crypto.randomUUID() + crypto.randomUUID();
          const encoder = new TextEncoder();
          const hashBuffer = await crypto.subtle.digest('SHA-256', encoder.encode(plainToken));
          const hashArray = Array.from(new Uint8Array(hashBuffer));
          const tokenHash = hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');

          const token = await this.opsServerRef.oneboxRef.database.createRegistryToken({
            name: config.name,
            tokenHash,
            type: config.type,
            scope: config.scope,
            expiresAt,
            createdAt: now,
            lastUsedAt: null,
            createdBy: identity.username,
          });

          let scopeDisplay: string;
          if (token.scope === 'all') {
            scopeDisplay = 'All services';
          } else if (Array.isArray(token.scope)) {
            scopeDisplay = token.scope.length === 1 ? token.scope[0] : `${token.scope.length} services`;
          } else {
            scopeDisplay = 'Unknown';
          }

          return {
            result: {
              token: {
                id: token.id!,
                name: token.name,
                type: token.type,
                scope: token.scope,
                scopeDisplay,
                expiresAt: token.expiresAt,
                createdAt: token.createdAt,
                lastUsedAt: token.lastUsedAt,
                createdBy: token.createdBy,
                isExpired: false,
              },
              plainToken,
            },
          };
        },
      ),
    );

    // Delete registry token
    this.typedrouter.addTypedHandler(
      new plugins.typedrequest.TypedHandler<interfaces.requests.IReq_DeleteRegistryToken>(
        'deleteRegistryToken',
        async (dataArg) => {
          await requireAdminIdentity(this.opsServerRef.adminHandler, dataArg);
          const token = await this.opsServerRef.oneboxRef.database.getRegistryTokenById(dataArg.tokenId);
          if (!token) {
            throw new plugins.typedrequest.TypedResponseError('Token not found');
          }
          await this.opsServerRef.oneboxRef.database.deleteRegistryToken(dataArg.tokenId);
          return { ok: true };
        },
      ),
    );
  }
}
