import { DatabaseConfig } from '../config/interfaces';
import { logger } from '../logging';
import { DatabaseManager } from './connection';
import { UserModel } from '../../modules/users/schemas/user.schema';
import * as bcrypt from 'bcryptjs';

export interface SeedData {
  users?: any[];
  roles?: any[];
  permissions?: any[];
  [key: string]: any[];
}

export interface SeederOptions {
  force?: boolean; // Force reseed even if data exists
  truncate?: boolean; // Truncate tables before seeding
  environment?: string; // Environment-specific seeding
}

export class DatabaseSeeder {
  private dbManager: DatabaseManager;
  private config: DatabaseConfig;

  constructor(dbManager: DatabaseManager, config: DatabaseConfig) {
    this.dbManager = dbManager;
    this.config = config;
  }

  /**
   * Run database seeding
   */
  async seed(options: SeederOptions = {}): Promise<void> {
    try {
      logger.info('[DatabaseSeeder] Starting database seeding...');

      // Check if seeding is needed
      if (!options.force && await this.hasData()) {
        logger.info('[DatabaseSeeder] Database already has data, skipping seeding');
        return;
      }

      // Truncate if requested
      if (options.truncate) {
        await this.truncate();
      }

      // Load seed data based on environment
      const seedData = await this.loadSeedData(options.environment);

      // Seed data
      await this.seedUsers(seedData.users || []);
      await this.seedRoles(seedData.roles || []);
      await this.seedPermissions(seedData.permissions || []);

      logger.info('[DatabaseSeeder] Database seeding completed successfully');
    } catch (error) {
      logger.error('[DatabaseSeeder] Failed to seed database:', error);
      throw error;
    }
  }

  /**
   * Check if database already has data
   */
  private async hasData(): Promise<boolean> {
    try {
      if (this.config.dialect === 'mongodb') {
        const userCount = await UserModel.countDocuments();
        return userCount > 0;
      } else {
        // For SQL databases, you'd check the users table
        // This is a placeholder for SQL implementation
        return false;
      }
    } catch (error) {
      logger.warn('[DatabaseSeeder] Could not check existing data:', error);
      return false;
    }
  }

  /**
   * Truncate all tables/collections
   */
  private async truncate(): Promise<void> {
    try {
      logger.info('[DatabaseSeeder] Truncating database...');
      
      if (this.config.dialect === 'mongodb') {
        await UserModel.deleteMany({});
        // Add other collections as needed
      } else {
        // For SQL databases, you'd truncate tables
        // This is a placeholder for SQL implementation
      }
      
      logger.info('[DatabaseSeeder] Database truncated successfully');
    } catch (error) {
      logger.error('[DatabaseSeeder] Failed to truncate database:', error);
      throw error;
    }
  }

  /**
   * Load seed data based on environment
   */
  private async loadSeedData(environment?: string): Promise<SeedData> {
    const env = environment || process.env.NODE_ENV || 'development';
    
    // Default seed data
    const defaultData: SeedData = {
      users: [
        {
          email: 'admin@diagramers.com',
          username: 'admin',
          password: '12345678@',
          firstName: 'Admin',
          lastName: 'User',
          role: 'admin',
          isActive: true,
          emailVerified: true,
          phoneVerified: false,
          permissions: ['*']
        },
        {
          email: 'user@diagramers.com',
          username: 'user',
          password: 'user123',
          firstName: 'Regular',
          lastName: 'User',
          role: 'user',
          isActive: true,
          emailVerified: true,
          phoneVerified: false,
          permissions: ['read:own', 'write:own']
        }
      ],
      roles: [
        { name: 'admin', description: 'Administrator with full access' },
        { name: 'moderator', description: 'Moderator with limited admin access' },
        { name: 'user', description: 'Regular user' }
      ],
      permissions: [
        { name: '*', description: 'All permissions' },
        { name: 'read:own', description: 'Read own data' },
        { name: 'write:own', description: 'Write own data' },
        { name: 'read:all', description: 'Read all data' },
        { name: 'write:all', description: 'Write all data' }
      ]
    };

    // Environment-specific overrides
    if (env === 'production') {
      // Production should have minimal seed data or none
      return {
        users: [],
        roles: defaultData.roles,
        permissions: defaultData.permissions
      };
    }

    if (env === 'testing') {
      // Testing environment with test data
      return {
        users: [
          {
            email: 'test@diagramers.com',
            username: 'testuser',
            password: 'test123',
            firstName: 'Test',
            lastName: 'User',
            role: 'user',
            isActive: true,
            emailVerified: true,
            phoneVerified: false,
            permissions: ['read:own']
          }
        ],
        roles: defaultData.roles,
        permissions: defaultData.permissions
      };
    }

    return defaultData;
  }

  /**
   * Seed users
   */
  private async seedUsers(users: any[]): Promise<void> {
    if (users.length === 0) return;

    logger.info(`[DatabaseSeeder] Seeding ${users.length} users...`);

    for (const userData of users) {
      try {
        // Check if user already exists
        const existingUser = await UserModel.findOne({ 
          $or: [
            { email: userData.email },
            { username: userData.username }
          ]
        });

        if (existingUser) {
          logger.info(`[DatabaseSeeder] User ${userData.email} already exists, skipping`);
          continue;
        }

        // Hash password
        const saltRounds = 10;
        const hashedPassword = await bcrypt.hash(userData.password, saltRounds);

        // Create user
        const user = new UserModel({
          ...userData,
          password: hashedPassword,
          email: userData.email.toLowerCase(),
          status: 1
        });

        await user.save();
        logger.info(`[DatabaseSeeder] Created user: ${userData.email}`);
      } catch (error) {
        logger.error(`[DatabaseSeeder] Failed to create user ${userData.email}:`, error);
      }
    }
  }

  /**
   * Seed roles (placeholder for role-based system)
   */
  private async seedRoles(roles: any[]): Promise<void> {
    if (roles.length === 0) return;

    logger.info(`[DatabaseSeeder] Seeding ${roles.length} roles...`);
    
    // This would create roles in a roles collection/table
    // For now, just log the roles
    for (const role of roles) {
      logger.info(`[DatabaseSeeder] Role: ${role.name} - ${role.description}`);
    }
  }

  /**
   * Seed permissions (placeholder for permission-based system)
   */
  private async seedPermissions(permissions: any[]): Promise<void> {
    if (permissions.length === 0) return;

    logger.info(`[DatabaseSeeder] Seeding ${permissions.length} permissions...`);
    
    // This would create permissions in a permissions collection/table
    // For now, just log the permissions
    for (const permission of permissions) {
      logger.info(`[DatabaseSeeder] Permission: ${permission.name} - ${permission.description}`);
    }
  }

  /**
   * Create custom seed data
   */
  async createSeedData(data: SeedData): Promise<void> {
    try {
      logger.info('[DatabaseSeeder] Creating custom seed data...');
      
      await this.seedUsers(data.users || []);
      await this.seedRoles(data.roles || []);
      await this.seedPermissions(data.permissions || []);
      
      logger.info('[DatabaseSeeder] Custom seed data created successfully');
    } catch (error) {
      logger.error('[DatabaseSeeder] Failed to create custom seed data:', error);
      throw error;
    }
  }

  /**
   * Reset database (truncate and reseed)
   */
  async reset(): Promise<void> {
    try {
      logger.info('[DatabaseSeeder] Resetting database...');
      await this.truncate();
      await this.seed({ force: true });
      logger.info('[DatabaseSeeder] Database reset completed');
    } catch (error) {
      logger.error('[DatabaseSeeder] Failed to reset database:', error);
      throw error;
    }
  }
} 