/**
 * Module Service
 *
 * Business logic for module operations including
 * listing, retrieving, and educational content access
 */

import { prisma } from '../../shared/database/prisma.client';
import { NotFoundError } from '../../shared/types';
import { logger } from '../../shared/utils/logger';
import type { modules, ModuleType, ExerciseDifficulty } from '@prisma/client';
import type {
  ModuleExample,
  ModuleExerciseSection,
} from '../../shared/types';

// ============================================
// TYPES
// ============================================

export interface ModuleListFilters {
  type?: ModuleType;
  isPublished?: boolean;
  difficulty?: ExerciseDifficulty;
  search?: string;
}

export interface ModuleListOptions {
  skip?: number;
  take?: number;
  orderBy?: 'order' | 'name' | 'createdAt';
  orderDirection?: 'asc' | 'desc';
}

export interface ModuleDetailOptions {
  includeExercises?: boolean;
  includeTopics?: boolean;
  includeProgress?: boolean;
  userId?: string | undefined;
}

export interface ModuleWithStats extends modules {
  _count?: {
    exercises: number;
    topics: number;
  };
  userProgress?: {
    exercisesCompleted: number;
    points: number;
    percentage: number;
    isStarted: boolean;
    isCompleted: boolean;
  } | null;
}

// ============================================
// SERVICE CLASS
// ============================================

class ModuleService {
  /**
   * List all modules with optional filtering
   */
  async listModules(
    filters: ModuleListFilters = {},
    options: ModuleListOptions = {}
  ): Promise<{ modules: ModuleWithStats[]; total: number }> {
    const { type, isPublished, difficulty, search } = filters;
    const { skip = 0, take = 50, orderBy = 'order', orderDirection = 'asc' } = options;

    const where: any = {};

    if (type !== undefined) {
      where.type = type;
    }

    if (isPublished !== undefined) {
      where.isPublished = isPublished;
    }

    if (difficulty !== undefined) {
      where.difficultyLevel = difficulty;
    }

    if (search) {
      where.OR = [
        { name: { contains: search, mode: 'insensitive' } },
        { description: { contains: search, mode: 'insensitive' } },
      ];
    }

    const [modules, total] = await Promise.all([
      prisma.modules.findMany({
        where,
        skip,
        take,
        orderBy: { [orderBy]: orderDirection },
        include: {
          _count: {
            select: {
              exercises: {
                where: { isPublished: true },
              },
              topics: true,
            },
          },
        },
      }),
      prisma.modules.count({ where }),
    ]);

    logger.info({
      count: modules.length,
      total,
      filters,
    }, 'Modules listed');

    return { modules, total };
  }

  /**
   * Get module by ID with detailed information
   */
  async getModuleById(
    id: string,
    options: ModuleDetailOptions = {}
  ): Promise<ModuleWithStats> {
    const { includeExercises = false, includeTopics = false, includeProgress = false, userId } = options;

    const module = await prisma.modules.findUnique({
      where: { id },
      include: {
        _count: {
          select: {
            exercises: {
              where: { isPublished: true },
            },
            topics: true,
          },
        },
        exercises: includeExercises
          ? {
              where: { isPublished: true },
              orderBy: { order: 'asc' },
              select: {
                id: true,
                type: true,
                difficulty: true,
                order: true,
                statement: true,
                points: true,
                timeLimitSeconds: true,
              },
            }
          : false,
        topics: includeTopics
          ? {
              orderBy: { order: 'asc' },
              select: {
                id: true,
                name: true,
                type: true,
                order: true,
                description: true,
              },
            }
          : false,
        progress: includeProgress && userId
          ? {
              where: { userId },
              select: {
                exercisesCompleted: true,
                points: true,
                percentage: true,
                isStarted: true,
                isCompleted: true,
              },
            }
          : false,
      },
    });

    if (!module) {
      throw new NotFoundError('Module');
    }

    // Transform progress to single object if exists
    const result: ModuleWithStats = module;
    if (includeProgress && userId && module.progress && module.progress.length > 0) {
      (result as any).userProgress = module.progress[0];
    }

    logger.info({ moduleId: id }, 'Module retrieved');

    return result;
  }

  /**
   * Get module introduction content
   */
  async getModuleIntroduction(id: string): Promise<{
    introduction: string | null;
    name: string;
    description: string;
    estimatedHours: number | null;
  }> {
    const module = await prisma.modules.findUnique({
      where: { id },
      select: {
        name: true,
        description: true,
        introduction: true,
        estimatedHours: true,
        isPublished: true,
      },
    });

    if (!module) {
      throw new NotFoundError('Module');
    }

    logger.info({ moduleId: id }, 'Module introduction retrieved');

    return {
      name: module.name,
      description: module.description,
      introduction: module.introduction,
      estimatedHours: module.estimatedHours,
    };
  }

  /**
   * Get module examples with LaTeX formulas
   */
  async getModuleExamples(id: string): Promise<{
    moduleName: string;
    examples: ModuleExample[];
  }> {
    const module = await prisma.modules.findUnique({
      where: { id },
      select: {
        name: true,
        examples: true,
        isPublished: true,
      },
    });

    if (!module) {
      throw new NotFoundError('Module');
    }

    const examples = (module.examples as unknown as ModuleExample[]) || [];

    logger.info({
      moduleId: id,
      examplesCount: examples.length,
    }, 'Module examples retrieved');

    return {
      moduleName: module.name,
      examples,
    };
  }

  /**
   * Get module exercise sections
   */
  async getModuleExerciseSections(id: string): Promise<{
    moduleName: string;
    sections: ModuleExerciseSection[];
  }> {
    const module = await prisma.modules.findUnique({
      where: { id },
      select: {
        name: true,
        exercises: true,
        isPublished: true,
      },
    });

    if (!module) {
      throw new NotFoundError('Module');
    }

    const sections = (module.exercises as unknown as ModuleExerciseSection[]) || [];

    logger.info({
      moduleId: id,
      sectionsCount: sections.length,
    }, 'Module exercise sections retrieved');

    return {
      moduleName: module.name,
      sections,
    };
  }

  /**
   * Get module with progress for a specific user
   */
  async getModuleWithUserProgress(
    moduleId: string,
    userId: string
  ): Promise<ModuleWithStats> {
    return this.getModuleById(moduleId, {
      includeExercises: true,
      includeTopics: true,
      includeProgress: true,
      userId,
    });
  }

  /**
   * Get modules by type
   */
  async getModulesByType(
    type: ModuleType,
    options: ModuleListOptions = {}
  ): Promise<{ modules: ModuleWithStats[]; total: number }> {
    return this.listModules({ type, isPublished: true }, options);
  }

  /**
   * Get module by order within type
   */
  async getModuleByOrder(
    type: ModuleType,
    order: number
  ): Promise<ModuleWithStats> {
    const module = await prisma.modules.findUnique({
      where: {
        type_order: {
          type,
          order,
        },
      },
      include: {
        _count: {
          select: {
            exercises: {
              where: { isPublished: true },
            },
            topics: true,
          },
        },
      },
    });

    if (!module) {
      throw new NotFoundError('Module');
    }

    logger.info({
      type,
      order,
      moduleId: module.id,
    }, 'Module retrieved by order');

    return module;
  }

  /**
   * Get next module in sequence
   */
  async getNextModule(currentModuleId: string): Promise<ModuleWithStats | null> {
    const currentModule = await prisma.modules.findUnique({
      where: { id: currentModuleId },
      select: {
        type: true,
        order: true,
      },
    });

    if (!currentModule) {
      throw new NotFoundError('Module');
    }

    const nextModule = await prisma.modules.findFirst({
      where: {
        type: currentModule.type,
        order: {
          gt: currentModule.order,
        },
        isPublished: true,
      },
      orderBy: {
        order: 'asc',
      },
      include: {
        _count: {
          select: {
            exercises: {
              where: { isPublished: true },
            },
            topics: true,
          },
        },
      },
    });

    if (!nextModule) {
      return null;
    }

    logger.info({
      currentModuleId,
      nextModuleId: nextModule.id,
    }, 'Next module retrieved');

    return nextModule;
  }

  /**
   * Get previous module in sequence
   */
  async getPreviousModule(currentModuleId: string): Promise<ModuleWithStats | null> {
    const currentModule = await prisma.modules.findUnique({
      where: { id: currentModuleId },
      select: {
        type: true,
        order: true,
      },
    });

    if (!currentModule) {
      throw new NotFoundError('Module');
    }

    const previousModule = await prisma.modules.findFirst({
      where: {
        type: currentModule.type,
        order: {
          lt: currentModule.order,
        },
        isPublished: true,
      },
      orderBy: {
        order: 'desc',
      },
      include: {
        _count: {
          select: {
            exercises: {
              where: { isPublished: true },
            },
            topics: true,
          },
        },
      },
    });

    if (!previousModule) {
      return null;
    }

    logger.info({
      currentModuleId,
      previousModuleId: previousModule.id,
    }, 'Previous module retrieved');

    return previousModule;
  }

  /**
   * Get module overview statistics
   */
  async getModuleOverview(moduleId: string): Promise<{
    totalExercises: number;
    exercisesByDifficulty: Record<string, number>;
    exercisesByType: Record<string, number>;
    estimatedHours: number;
    topicCount: number;
  }> {
    const module = await prisma.modules.findUnique({
      where: { id: moduleId },
      include: {
        _count: {
          select: {
            exercises: {
              where: { isPublished: true },
            },
            topics: true,
          },
        },
        exercises: {
          where: { isPublished: true },
          select: {
            difficulty: true,
            type: true,
          },
        },
      },
    });

    if (!module) {
      throw new NotFoundError('Module');
    }

    const exercisesByDifficulty: Record<string, number> = {};
    const exercisesByType: Record<string, number> = {};

    for (const exercise of module.exercises) {
      exercisesByDifficulty[exercise.difficulty] =
        (exercisesByDifficulty[exercise.difficulty] || 0) + 1;
      exercisesByType[exercise.type] =
        (exercisesByType[exercise.type] || 0) + 1;
    }

    return {
      totalExercises: module._count.exercises,
      exercisesByDifficulty,
      exercisesByType,
      estimatedHours: module.estimatedHours || 0,
      topicCount: module._count.topics,
    };
  }
}

// ============================================
// EXPORT
// ============================================

export const moduleService = new ModuleService();
export default moduleService;
