import type { IMcpServerRepository, ISecretRepository } from '../../repositories/interfaces.js';
import type { IProjectRepository } from '../../repositories/project.repository.js';
import type { IUserRepository } from '../../repositories/user.repository.js';
import type { IGroupRepository } from '../../repositories/group.repository.js';
import type { IRbacDefinitionRepository } from '../../repositories/rbac-definition.repository.js';
import type { IPromptRepository } from '../../repositories/prompt.repository.js';
import type { ITemplateRepository } from '../../repositories/template.repository.js';
import { encrypt, isSensitiveKey } from './crypto.js';
import type { EncryptedPayload } from './crypto.js';
import { APP_VERSION } from '@mcpctl/shared';

export interface BackupBundle {
  version: string;
  mcpctlVersion: string;
  createdAt: string;
  encrypted: boolean;
  servers: BackupServer[];
  secrets: BackupSecret[];
  projects: BackupProject[];
  users?: BackupUser[];
  groups?: BackupGroup[];
  rbacBindings?: BackupRbacBinding[];
  prompts?: BackupPrompt[];
  templates?: BackupTemplate[];
  encryptedSecrets?: EncryptedPayload;
}

export interface BackupServer {
  name: string;
  description: string;
  packageName: string | null;
  runtime: string | null;
  dockerImage: string | null;
  transport: string;
  repositoryUrl: string | null;
  externalUrl: string | null;
  command: unknown;
  containerPort: number | null;
  replicas: number;
  env: unknown;
  healthCheck: unknown;
}

export interface BackupSecret {
  name: string;
  data: Record<string, string>;
}

export interface BackupProject {
  name: string;
  description: string;
  proxyMode?: string;
  proxyModel?: string;
  llmProvider?: string | null;
  llmModel?: string | null;
  serverNames?: string[];
}

export interface BackupUser {
  email: string;
  name: string | null;
  role: string;
  provider: string | null;
}

export interface BackupGroup {
  name: string;
  description: string;
  memberEmails: string[];
}

export interface BackupRbacBinding {
  name: string;
  subjects: unknown;
  roleBindings: unknown;
}

export interface BackupPrompt {
  name: string;
  content: string;
  projectName: string | null;
  priority: number;
  summary: string | null;
  chapters: unknown;
  linkTarget: string | null;
}

export interface BackupTemplate {
  name: string;
  description: string;
  packageName: string | null;
  dockerImage: string | null;
  transport: string;
  command: unknown;
  containerPort: number | null;
  env: unknown;
  healthCheck: unknown;
}

export interface BackupOptions {
  password?: string;
  resources?: Array<'servers' | 'secrets' | 'projects' | 'users' | 'groups' | 'rbac' | 'prompts' | 'templates'>;
}

export class BackupService {
  constructor(
    private serverRepo: IMcpServerRepository,
    private projectRepo: IProjectRepository,
    private secretRepo: ISecretRepository,
    private userRepo?: IUserRepository,
    private groupRepo?: IGroupRepository,
    private rbacRepo?: IRbacDefinitionRepository,
    private promptRepo?: IPromptRepository,
    private templateRepo?: ITemplateRepository,
  ) {}

  async createBackup(options?: BackupOptions): Promise<BackupBundle> {
    const resources = options?.resources ?? ['servers', 'secrets', 'projects', 'users', 'groups', 'rbac', 'prompts', 'templates'];

    let servers: BackupServer[] = [];
    let secrets: BackupSecret[] = [];
    let projects: BackupProject[] = [];
    let users: BackupUser[] = [];
    let groups: BackupGroup[] = [];
    let rbacBindings: BackupRbacBinding[] = [];

    if (resources.includes('servers')) {
      const allServers = await this.serverRepo.findAll();
      servers = allServers.map((s) => ({
        name: s.name,
        description: s.description,
        packageName: s.packageName,
        runtime: s.runtime,
        dockerImage: s.dockerImage,
        transport: s.transport,
        repositoryUrl: s.repositoryUrl,
        externalUrl: s.externalUrl,
        command: s.command,
        containerPort: s.containerPort,
        replicas: s.replicas,
        env: s.env,
        healthCheck: s.healthCheck,
      }));
    }

    if (resources.includes('secrets')) {
      const allSecrets = await this.secretRepo.findAll();
      secrets = allSecrets.map((s) => ({
        name: s.name,
        data: s.data as Record<string, string>,
      }));
    }

    if (resources.includes('projects')) {
      const allProjects = await this.projectRepo.findAll();
      projects = allProjects.map((proj) => ({
        name: proj.name,
        description: proj.description,
        proxyModel: proj.proxyModel,
        llmProvider: proj.llmProvider,
        llmModel: proj.llmModel,
        serverNames: proj.servers.map((ps) => ps.server.name),
      }));
    }

    if (resources.includes('users') && this.userRepo) {
      const allUsers = await this.userRepo.findAll();
      users = allUsers.map((u) => ({
        email: u.email,
        name: u.name,
        role: u.role,
        provider: u.provider,
      }));
    }

    if (resources.includes('groups') && this.groupRepo) {
      const allGroups = await this.groupRepo.findAll();
      groups = allGroups.map((g) => ({
        name: g.name,
        description: g.description,
        memberEmails: g.members.map((m) => m.user.email),
      }));
    }

    if (resources.includes('rbac') && this.rbacRepo) {
      const allRbac = await this.rbacRepo.findAll();
      rbacBindings = allRbac.map((r) => ({
        name: r.name,
        subjects: r.subjects,
        roleBindings: r.roleBindings,
      }));
    }

    let prompts: BackupPrompt[] = [];
    let templates: BackupTemplate[] = [];

    if (resources.includes('prompts') && this.promptRepo) {
      const allPrompts = await this.promptRepo.findAll();
      prompts = allPrompts.map((p) => ({
        name: p.name,
        content: p.content,
        projectName: (p as unknown as { project?: { name: string } }).project?.name ?? null,
        priority: p.priority,
        summary: p.summary,
        chapters: p.chapters,
        linkTarget: p.linkTarget,
      }));
    }

    if (resources.includes('templates') && this.templateRepo) {
      const allTemplates = await this.templateRepo.findAll();
      templates = allTemplates.map((t) => ({
        name: t.name,
        description: t.description,
        packageName: t.packageName,
        dockerImage: t.dockerImage,
        transport: t.transport,
        command: t.command,
        containerPort: t.containerPort,
        env: t.env,
        healthCheck: t.healthCheck,
      }));
    }

    const bundle: BackupBundle = {
      version: '1',
      mcpctlVersion: APP_VERSION,
      createdAt: new Date().toISOString(),
      encrypted: false,
      servers,
      secrets,
      projects,
      users,
      groups,
      rbacBindings,
      prompts,
      templates,
    };

    if (options?.password && secrets.length > 0) {
      // Collect sensitive values from secrets and encrypt them
      const sensitiveData: Record<string, string> = {};
      for (const secret of secrets) {
        for (const [key, value] of Object.entries(secret.data)) {
          if (isSensitiveKey(key)) {
            const secretKey = `secret:${secret.name}:${key}`;
            sensitiveData[secretKey] = value;
            secret.data[key] = `__ENCRYPTED:${secretKey}__`;
          }
        }
      }

      if (Object.keys(sensitiveData).length > 0) {
        bundle.encrypted = true;
        bundle.encryptedSecrets = encrypt(JSON.stringify(sensitiveData), options.password);
      }
    }

    return bundle;
  }
}
