import { Injectable, Logger } from '@nestjs/common';
import {
  db,
  InitAppConfigs,
  InitRoles,
  InitTaxonomies,
  ObjectType,
} from '@nice/common';
import { AuthService } from '@server/auth/auth.service';
import { MinioService } from '@server/utils/minio/minio.service';
import { AppConfigService } from '@server/models/app-config/app-config.service';
import { GenDevService } from './gendev.service';

@Injectable()
export class InitService {
  private readonly logger = new Logger(InitService.name);
  constructor(
    private readonly appConfigService: AppConfigService,
    private readonly minioService: MinioService,
    private readonly authService: AuthService,
    private readonly genDevService: GenDevService,
  ) {}
  private async createRoles() {
    this.logger.log('Checking existing system roles');
    for (const role of InitRoles) {
      const existingRole = await db.role.findUnique({
        where: { name: role.name },
      });
      if (!existingRole) {
        this.logger.log(`Creating role: ${role.name}`);
        await db.role.create({
          data: { ...role, system: true },
        });
      } else {
        this.logger.log(`Role already exists: ${role.name}`);
      }
    }
  }
  private async createOrUpdateTaxonomy() {
    this.logger.log('Checking existing taxonomies');
    const existingTaxonomies = await db.taxonomy.findMany();
    const existingTaxonomyMap = new Map(
      existingTaxonomies.map((taxonomy) => [taxonomy.slug, taxonomy]),
    );

    for (const [index, taxonomy] of InitTaxonomies.entries()) {
      const existingTaxonomy = existingTaxonomyMap.get(taxonomy.slug);
      if (!existingTaxonomy) {
        // Create new taxonomy
        await db.taxonomy.create({
          data: {
            ...taxonomy,
            order: index,
          },
        });
        this.logger.log(`Created new taxonomy: ${taxonomy.name}`);
      } else if(process.env.NODE_ENV === 'development'){
        // Check for differences and update if necessary
        const differences = Object.keys(taxonomy).filter(
          (key) => taxonomy[key] !== existingTaxonomy[key],
        );
      
        if (differences.length > 0) {
          await db.taxonomy.update({
            where: { id: existingTaxonomy.id },
            data: {
              ...taxonomy,
              order: index,
            },
          });
          this.logger.log(`Updated taxonomy: ${taxonomy.name}`);
        } else {
          this.logger.log(`No changes for taxonomy: ${taxonomy.name}`);
        }
      }
    }
  }
  private async createRoot() {
    this.logger.log('Checking for root account');

    const rootAccountExists = await db.staff.findFirst({
      where: {
        OR: [
          {
            phoneNumber: process.env.ADMIN_PHONE_NUMBER || '000000',
          },
          {
            username: 'root',
          },
        ],
      },
    });

    if (!rootAccountExists) {
      this.logger.log('Creating root account');
      const rootStaff = await this.authService.signUp({
        username: 'root',
        password: 'root',
      });
      const rootRole = await db.role.findUnique({
        where: { name: '根管理员' },
      });

      if (rootRole) {
        this.logger.log('Assigning root role to root account');
        await db.roleMap.create({
          data: {
            objectType: ObjectType.STAFF,
            objectId: rootStaff.id,
            roleId: rootRole.id,
          },
        });
      } else {
        this.logger.error('Root role does not exist');
      }
    } else {
      this.logger.log('Root account already exists');
    }
  }
  private async createBucket() {
    await this.minioService.createBucket('app');
  }
  private async initAppConfigs() {
    const existingConfigs = await db.appConfig.findMany();
    const existingConfigSlugs = existingConfigs.map((config) => config.slug);
    for (const [index, config] of InitAppConfigs.entries()) {
      if (!existingConfigSlugs.includes(config.slug)) {
        this.logger.log(`create Option Page ${config.title}`);
        await this.appConfigService.create({ data: config });
      } else {
        this.logger.log(`AppConfig already exists: ${config.title}`);
      }
    }
  }
  async init() {
    try {
      this.logger.log('Starting system initialization');

      await this.createRoles();
      await this.createRoot();
      await this.createOrUpdateTaxonomy();
      await this.initAppConfigs();
      try {
        this.logger.log('Initialize minio');
        await this.createBucket();
      } catch (error) {
        this.logger.error('Minio initialization failed:', error);
      }

      if (process.env.NODE_ENV === 'development') {
        try {
          await this.genDevService.genDataEvent();
        } catch (error) {
          this.logger.error('Development data generation failed:', error);
          // Not throwing here as this is development-only
        }
      }

      this.logger.log('System initialization completed successfully');
    } catch (error) {
      this.logger.error('System initialization failed:', error);
    }
  }
}
