import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, MoreThan } from 'typeorm';
import { User } from '../../entities/user.entity';
import { App } from '../../entities/app.entity';
import { Build } from '../../entities/build.entity';
import { PlatformSettings } from '../../entities/platform-settings.entity';

@Injectable()
export class AdminService {
  constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>,
    @InjectRepository(App)
    private appRepository: Repository<App>,
    @InjectRepository(Build)
    private buildRepository: Repository<Build>,
    @InjectRepository(PlatformSettings)
    private settingsRepository: Repository<PlatformSettings>,
  ) { }

  async getDashboardStats() {
    const totalUsers = await this.userRepository.count();
    const activeUsers = await this.userRepository.count({ where: { isActive: true } });
    const totalBuilds = await this.buildRepository.count();

    // Unique apps: NoteABC iOS + NoteABC Android = 1 app
    const totalApps = await this.getUniqueAppCount();

    // Get active users in last 30 days
    const thirtyDaysAgo = new Date();
    thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
    const activeUsersLast30Days = await this.userRepository.count({
      where: {
        lastActiveAt: MoreThan(thirtyDaysAgo),
      },
    });

    // Get users by month (last 6 months)
    const usersByMonth = await this.getUsersByMonth(6);
    const buildsByMonth = await this.getBuildsByMonth(6);

    // Get platform distribution
    const androidApps = await this.appRepository.count({ where: { platform: 'android' } });
    const iosApps = await this.appRepository.count({ where: { platform: 'ios' } });

    return {
      totalUsers,
      activeUsers,
      inactiveUsers: totalUsers - activeUsers,
      totalApps,
      totalBuilds,
      activeUsersLast30Days,
      platformDistribution: {
        android: androidApps,
        ios: iosApps,
      },
      usersByMonth,
      buildsByMonth,
    };
  }

  async getAllUsers(page = 1, limit = 20, search?: string) {
    const query = this.userRepository
      .createQueryBuilder('user')
      .leftJoinAndSelect('user.apps', 'apps')
      .leftJoinAndSelect('apps.builds', 'builds')
      .orderBy('user.createdAt', 'DESC');

    if (search) {
      query.where(
        'user.name ILIKE :search OR user.email ILIKE :search',
        { search: `%${search}%` }
      );
    }

    const [users, total] = await query
      .skip((page - 1) * limit)
      .take(limit)
      .getManyAndCount();

    const usersWithStats = users.map(user => ({
      id: user.id,
      name: user.name,
      email: user.email,
      profilePhoto: user.profilePhoto,
      role: user.role,
      isActive: user.isActive,
      createdAt: user.createdAt,
      lastActiveAt: user.lastActiveAt,
      totalApps: user.apps?.length || 0,
      totalBuilds: user.apps?.reduce((sum, app) => sum + (app.builds?.length || 0), 0) || 0,
    }));

    return {
      users: usersWithStats,
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  async getUserDetails(userId: string) {
    const user = await this.userRepository.findOne({
      where: { id: userId },
      relations: ['apps', 'apps.builds'],
    });

    if (!user) {
      throw new NotFoundException('User not found');
    }

    const totalBuilds = user.apps.reduce((sum, app) => sum + (app.builds?.length || 0), 0);
    const totalSize = user.apps.reduce(
      (sum, app) => sum + (app.builds?.reduce((s, b) => s + Number(b.size), 0) || 0),
      0
    );

    return {
      id: user.id,
      name: user.name,
      email: user.email,
      profilePhoto: user.profilePhoto,
      role: user.role,
      isActive: user.isActive,
      createdAt: user.createdAt,
      lastActiveAt: user.lastActiveAt,
      apps: user.apps.map(app => ({
        id: app.id,
        appName: app.appName,
        platform: app.platform,
        bundleOrPackage: app.bundleOrPackage,
        iconFileId: (app.iconData || app.iconFileId) ? app.id : null,
        builds: (app.builds || []).map(b => ({
          id: b.id,
          version: b.version,
          buildNumber: b.buildNumber,
          size: b.size,
          createdAt: b.createdAt,
          iconFileId: (app.iconData || app.iconFileId) ? app.id : null,
          minOs: b.minOs,
          shortCode: b.shortCode,
          isPasswordProtected: b.isPasswordProtected,
          passwordText: b.passwordText,
        })),
      })),
      stats: {
        totalApps: user.apps.length,
        totalBuilds,
        totalSize,
      },
      userSettings: {
        buildLinkExpiryDays: user.buildLinkExpiryDays,
        aiReleaseNotesEnabled: user.aiReleaseNotesEnabled,
        maxBuildSizeMb: user.maxBuildSizeMb,
        cicdEnabled: user.cicdEnabled,
        slackEnabled: user.slackEnabled,
      },
      slackStatus: {
        connected: !!(user.slackWebhookUrl || user.slackAccessToken),
        channelId: user.slackChannelId,
        workspaceId: user.slackWorkspaceId,
      },
    };
  }

  async updateUserStatus(userId: string, isActive: boolean) {
    const user = await this.userRepository.findOne({ where: { id: userId } });
    if (!user) {
      throw new NotFoundException('User not found');
    }

    user.isActive = isActive;
    await this.userRepository.save(user);

    return { success: true, user };
  }

  async updateUserRole(userId: string, role: string) {
    const user = await this.userRepository.findOne({ where: { id: userId } });
    if (!user) {
      throw new NotFoundException('User not found');
    }

    user.role = role;
    await this.userRepository.save(user);

    return { success: true, user };
  }

  async getRecentBuilds(limit = 10) {
    const builds = await this.buildRepository.find({
      relations: ['app', 'app.user'],
      order: { createdAt: 'DESC' },
      take: limit,
    });

    return builds.map(build => ({
      id: build.id,
      version: build.version,
      buildNumber: build.buildNumber,
      platform: build.app.platform,
      appName: build.app.appName,
      iconFileId: (build.app.iconData || build.app.iconFileId) ? build.app.id : null,
      createdAt: build.createdAt,
      size: build.size,
      userName: build.app.user.name,
      userEmail: build.app.user.email,
      shortCode: build.shortCode,
      isPasswordProtected: build.isPasswordProtected,
      passwordText: build.passwordText,
    }));
  }

  async getRecentUsers(limit = 10) {
    const users = await this.userRepository.find({
      order: { createdAt: 'DESC' },
      take: limit,
      relations: ['apps'],
    });

    return users.map(user => ({
      id: user.id,
      name: user.name,
      email: user.email,
      profilePhoto: user.profilePhoto,
      role: user.role,
      isActive: user.isActive,
      createdAt: user.createdAt,
      totalApps: user.apps?.length || 0,
    }));
  }

  async getAllAppsWithIcons(limit = 50) {
    const apps = await this.appRepository
      .createQueryBuilder('app')
      .leftJoinAndSelect('app.user', 'user')
      .leftJoinAndSelect('app.builds', 'builds')
      .where('app.iconData IS NOT NULL')
      .orWhere("app.iconFileId IS NOT NULL AND app.iconFileId != ''")
      .orderBy('app.createdAt', 'DESC')
      .take(limit)
      .getMany();

    return apps.map(app => ({
      id: app.id,
      appName: app.appName,
      platform: app.platform,
      iconFileId: app.id,
      userName: app.user.name,
      totalBuilds: app.builds?.length || 0,
      createdAt: app.createdAt,
    }));
  }

  private async getUsersByMonth(months: number) {
    const result = await this.userRepository
      .createQueryBuilder('user')
      .select("TO_CHAR(user.created_at, 'YYYY-MM')", 'month')
      .addSelect('COUNT(*)', 'count')
      .where(`user.created_at >= NOW() - INTERVAL '${months} months'`)
      .groupBy('month')
      .orderBy('month', 'ASC')
      .getRawMany();

    return result.map(r => ({ month: r.month, count: parseInt(r.count) }));
  }

  private async getBuildsByMonth(months: number) {
    const result = await this.buildRepository
      .createQueryBuilder('build')
      .select("TO_CHAR(build.created_at, 'YYYY-MM')", 'month')
      .addSelect('COUNT(*)', 'count')
      .where(`build.created_at >= NOW() - INTERVAL '${months} months'`)
      .groupBy('month')
      .orderBy('month', 'ASC')
      .getRawMany();

    return result.map(r => ({ month: r.month, count: parseInt(r.count) }));
  }

  async deleteUser(userId: string) {
    const user = await this.userRepository.findOne({ where: { id: userId } });
    if (!user) {
      throw new NotFoundException('User not found');
    }

    await this.userRepository.remove(user);
    return { success: true, message: 'User deleted successfully' };
  }

  async getAllBuilds(page = 1, limit = 20, search?: string) {
    const query = this.buildRepository
      .createQueryBuilder('build')
      .leftJoinAndSelect('build.app', 'app')
      .leftJoinAndSelect('app.user', 'user')
      .orderBy('build.createdAt', 'DESC');

    if (search) {
      query.where(
        'app.appName ILIKE :search OR user.name ILIKE :search OR user.email ILIKE :search',
        { search: `%${search}%` },
      );
    }

    const [builds, total] = await query
      .skip((page - 1) * limit)
      .take(limit)
      .getManyAndCount();

    return {
      builds: builds.map(build => ({
        id: build.id,
        version: build.version,
        buildNumber: build.buildNumber,
        platform: build.app.platform,
        appName: build.app.appName,
        iconFileId: (build.app.iconData || build.app.iconFileId) ? build.app.id : null,
        createdAt: build.createdAt,
        size: build.size,
        userName: build.app.user.name,
        userEmail: build.app.user.email,
        userId: build.app.user.id,
        shortCode: build.shortCode,
        isPasswordProtected: build.isPasswordProtected,
        passwordText: build.passwordText,
      })),
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  async getActiveUsers(page = 1, limit = 20) {
    const thirtyDaysAgo = new Date();
    thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);

    const query = this.userRepository
      .createQueryBuilder('user')
      .leftJoinAndSelect('user.apps', 'apps')
      .leftJoinAndSelect('apps.builds', 'builds')
      .where('user.lastActiveAt >= :thirtyDaysAgo', { thirtyDaysAgo })
      .orderBy('user.lastActiveAt', 'DESC');

    const [users, total] = await query
      .skip((page - 1) * limit)
      .take(limit)
      .getManyAndCount();

    return {
      users: users.map(user => ({
        id: user.id,
        name: user.name,
        email: user.email,
        profilePhoto: user.profilePhoto,
        role: user.role,
        isActive: user.isActive,
        createdAt: user.createdAt,
        lastActiveAt: user.lastActiveAt,
        totalApps: user.apps?.length || 0,
        totalBuilds: user.apps?.reduce((sum, app) => sum + (app.builds?.length || 0), 0) || 0,
      })),
      total,
      page,
      limit,
      totalPages: Math.ceil(total / limit),
    };
  }

  async getUniqueApps(page = 1, limit = 20, search?: string) {
    // Fetch all apps with their relations
    const query = this.appRepository
      .createQueryBuilder('app')
      .leftJoinAndSelect('app.user', 'user')
      .leftJoinAndSelect('app.builds', 'builds')
      .orderBy('app.appName', 'ASC');

    if (search) {
      query.where('app.appName ILIKE :search', { search: `%${search}%` });
    }

    const allApps = await query.getMany();

    // Group by appName
    const groupedMap = new Map<string, {
      appName: string;
      platforms: Set<string>;
      uploaders: Map<string, { id: string; name: string; email: string; profilePhoto: string | null }>;
      totalBuilds: number;
      iconAppId: string | null;
      latestCreatedAt: Date;
    }>();

    for (const app of allApps) {
      const key = app.appName.toLowerCase().trim();
      if (!groupedMap.has(key)) {
        groupedMap.set(key, {
          appName: app.appName,
          platforms: new Set(),
          uploaders: new Map(),
          totalBuilds: 0,
          iconAppId: (app.iconData || app.iconFileId) ? app.id : null,
          latestCreatedAt: app.createdAt,
        });
      }
      const entry = groupedMap.get(key)!;
      entry.platforms.add(app.platform);
      entry.uploaders.set(app.user.id, {
        id: app.user.id,
        name: app.user.name,
        email: app.user.email,
        profilePhoto: app.user.profilePhoto,
      });
      entry.totalBuilds += app.builds?.length || 0;
      if (!entry.iconAppId && (app.iconData || app.iconFileId)) entry.iconAppId = app.id;
      if (app.createdAt > entry.latestCreatedAt) entry.latestCreatedAt = app.createdAt;
    }

    const uniqueApps = Array.from(groupedMap.values()).map(entry => ({
      appName: entry.appName,
      platforms: Array.from(entry.platforms),
      uploaders: Array.from(entry.uploaders.values()),
      totalBuilds: entry.totalBuilds,
      iconFileId: entry.iconAppId,
      latestCreatedAt: entry.latestCreatedAt,
    }));

    // Sort by appName
    uniqueApps.sort((a, b) => a.appName.localeCompare(b.appName));

    const total = uniqueApps.length;
    const totalPages = Math.ceil(total / limit);
    const paginated = uniqueApps.slice((page - 1) * limit, page * limit);

    return { apps: paginated, total, page, limit, totalPages };
  }

  async getUniqueAppCount(): Promise<number> {
    const result = await this.appRepository
      .createQueryBuilder('app')
      .select('LOWER(TRIM(app.appName))', 'normalizedName')
      .groupBy('LOWER(TRIM(app.appName))')
      .getRawMany();
    return result.length;
  }

  async updateUserSettings(userId: string, updates: {
    buildLinkExpiryDays?: number | null;
    aiReleaseNotesEnabled?: boolean | null;
    maxBuildSizeMb?: number | null;
    cicdEnabled?: boolean | null;
    slackEnabled?: boolean | null;
  }) {
    const user = await this.userRepository.findOne({ where: { id: userId } });
    if (!user) throw new NotFoundException('User not found');

    if (updates.buildLinkExpiryDays !== undefined) {
      user.buildLinkExpiryDays = updates.buildLinkExpiryDays === null
        ? null
        : Math.max(0, updates.buildLinkExpiryDays);
    }
    if (updates.aiReleaseNotesEnabled !== undefined) {
      user.aiReleaseNotesEnabled = updates.aiReleaseNotesEnabled;
    }
    if (updates.maxBuildSizeMb !== undefined) {
      user.maxBuildSizeMb = updates.maxBuildSizeMb === null
        ? null
        : Math.max(0, updates.maxBuildSizeMb);
    }
    if (updates.cicdEnabled !== undefined) {
      user.cicdEnabled = updates.cicdEnabled;
    }
    if (updates.slackEnabled !== undefined) {
      user.slackEnabled = updates.slackEnabled;
    }
    await this.userRepository.save(user);
    return {
      success: true,
      userSettings: {
        buildLinkExpiryDays: user.buildLinkExpiryDays,
        aiReleaseNotesEnabled: user.aiReleaseNotesEnabled,
        maxBuildSizeMb: user.maxBuildSizeMb,
        cicdEnabled: user.cicdEnabled,
        slackEnabled: user.slackEnabled,
      },
    };
  }

  async getSettings(): Promise<PlatformSettings> {
    let settings = await this.settingsRepository.findOne({ where: { id: 'global' } });
    if (!settings) {
      settings = this.settingsRepository.create({
        id: 'global',
        buildLinkExpiryDays: 0,
        aiReleaseNotesEnabled: true,
        maxBuildSizeMb: 500,
        cicdEnabled: true,
        slackEnabled: false,
      });
      await this.settingsRepository.save(settings);
    }
    return settings;
  }

  async updateSettings(updates: {
    buildLinkExpiryDays?: number;
    aiReleaseNotesEnabled?: boolean;
    maxBuildSizeMb?: number;
    cicdEnabled?: boolean;
    slackEnabled?: boolean;
  }): Promise<PlatformSettings> {
    const settings = await this.getSettings();
    if (updates.buildLinkExpiryDays !== undefined) {
      settings.buildLinkExpiryDays = Math.max(0, updates.buildLinkExpiryDays);
    }
    if (updates.aiReleaseNotesEnabled !== undefined) {
      settings.aiReleaseNotesEnabled = updates.aiReleaseNotesEnabled;
    }
    if (updates.maxBuildSizeMb !== undefined) {
      settings.maxBuildSizeMb = Math.max(0, updates.maxBuildSizeMb);
    }
    if (updates.cicdEnabled !== undefined) {
      settings.cicdEnabled = updates.cicdEnabled;
    }
    if (updates.slackEnabled !== undefined) {
      settings.slackEnabled = updates.slackEnabled;
    }
    return this.settingsRepository.save(settings);
  }
}
