/**
 * Course Service
 * Handles course management operations
 */

import { db } from '../../../shared/database';
import { logger } from '../../../shared/utils/logger';
import type {
  Course,
  CourseWithDetails,
  CreateCourseInput,
  UpdateCourseInput,
  CourseFilters,
  Category,
  CreateCategoryInput,
  Module,
  ModuleWithLessons,
  CreateModuleInput,
  Lesson,
  CreateLessonInput,
  PaginatedResult,
  PaginationOptions,
} from '../types/education.types';

// ============================================================================
// Helper Functions
// ============================================================================

function generateSlug(title: string): string {
  return title
    .toLowerCase()
    .replace(/[^a-z0-9\s-]/g, '')
    .replace(/\s+/g, '-')
    .replace(/-+/g, '-')
    .trim();
}

function transformCourse(row: Record<string, unknown>): Course {
  return {
    id: row.id as string,
    title: row.title as string,
    slug: row.slug as string,
    description: row.description as string | undefined,
    shortDescription: row.short_description as string | undefined,
    thumbnailUrl: row.thumbnail_url as string | undefined,
    previewVideoUrl: row.preview_video_url as string | undefined,
    categoryId: row.category_id as string | undefined,
    level: row.level as Course['level'],
    tags: (row.tags as string[]) || [],
    isFree: row.is_free as boolean,
    price: parseFloat(row.price as string) || 0,
    currency: row.currency as string,
    requiresSubscription: row.requires_subscription as boolean,
    minSubscriptionTier: row.min_subscription_tier as string | undefined,
    durationMinutes: row.duration_minutes as number | undefined,
    lessonsCount: row.lessons_count as number,
    enrolledCount: row.enrolled_count as number,
    averageRating: parseFloat(row.average_rating as string) || 0,
    ratingsCount: row.ratings_count as number,
    status: row.status as Course['status'],
    publishedAt: row.published_at ? new Date(row.published_at as string) : undefined,
    instructorId: row.instructor_id as string | undefined,
    aiGenerated: row.ai_generated as boolean,
    createdAt: new Date(row.created_at as string),
    updatedAt: new Date(row.updated_at as string),
  };
}

function transformLesson(row: Record<string, unknown>): Lesson {
  return {
    id: row.id as string,
    moduleId: row.module_id as string,
    courseId: row.course_id as string,
    title: row.title as string,
    slug: row.slug as string,
    contentType: row.content_type as Lesson['contentType'],
    videoUrl: row.video_url as string | undefined,
    videoDurationSeconds: row.video_duration_seconds as number | undefined,
    videoProvider: row.video_provider as string | undefined,
    contentMarkdown: row.content_markdown as string | undefined,
    contentHtml: row.content_html as string | undefined,
    resources: (row.resources as Lesson['resources']) || [],
    sortOrder: row.sort_order as number,
    isPreview: row.is_preview as boolean,
    aiGenerated: row.ai_generated as boolean,
    aiSummary: row.ai_summary as string | undefined,
    createdAt: new Date(row.created_at as string),
    updatedAt: new Date(row.updated_at as string),
  };
}

// ============================================================================
// Course Service Class
// ============================================================================

class CourseService {
  // ==========================================================================
  // Categories
  // ==========================================================================

  async getCategories(): Promise<Category[]> {
    const result = await db.query<Record<string, unknown>>(
      `SELECT * FROM education.categories ORDER BY sort_order, name`
    );
    return result.rows.map((row) => ({
      id: row.id as string,
      name: row.name as string,
      slug: row.slug as string,
      description: row.description as string | undefined,
      icon: row.icon as string | undefined,
      parentId: row.parent_id as string | undefined,
      sortOrder: row.sort_order as number,
      createdAt: new Date(row.created_at as string),
      updatedAt: new Date(row.updated_at as string),
    }));
  }

  async getCategoryById(id: string): Promise<Category | null> {
    const result = await db.query<Record<string, unknown>>(
      `SELECT * FROM education.categories WHERE id = $1`,
      [id]
    );
    if (result.rows.length === 0) return null;
    const row = result.rows[0];
    return {
      id: row.id as string,
      name: row.name as string,
      slug: row.slug as string,
      description: row.description as string | undefined,
      icon: row.icon as string | undefined,
      parentId: row.parent_id as string | undefined,
      sortOrder: row.sort_order as number,
      createdAt: new Date(row.created_at as string),
      updatedAt: new Date(row.updated_at as string),
    };
  }

  async createCategory(input: CreateCategoryInput): Promise<Category> {
    const slug = input.slug || generateSlug(input.name);
    const result = await db.query<Record<string, unknown>>(
      `INSERT INTO education.categories (name, slug, description, icon, parent_id, sort_order)
       VALUES ($1, $2, $3, $4, $5, $6)
       RETURNING *`,
      [input.name, slug, input.description, input.icon, input.parentId, input.sortOrder || 0]
    );
    const row = result.rows[0];
    return {
      id: row.id as string,
      name: row.name as string,
      slug: row.slug as string,
      description: row.description as string | undefined,
      icon: row.icon as string | undefined,
      parentId: row.parent_id as string | undefined,
      sortOrder: row.sort_order as number,
      createdAt: new Date(row.created_at as string),
      updatedAt: new Date(row.updated_at as string),
    };
  }

  // ==========================================================================
  // Courses
  // ==========================================================================

  async getCourses(
    filters: CourseFilters = {},
    pagination: PaginationOptions = {}
  ): Promise<PaginatedResult<Course>> {
    const { page = 1, pageSize = 20, sortBy = 'created_at', sortOrder = 'desc' } = pagination;
    const offset = (page - 1) * pageSize;

    const conditions: string[] = [];
    const params: (string | number | boolean | null)[] = [];
    let paramIndex = 1;

    if (filters.categoryId) {
      conditions.push(`category_id = $${paramIndex++}`);
      params.push(filters.categoryId);
    }
    if (filters.level) {
      conditions.push(`level = $${paramIndex++}`);
      params.push(filters.level);
    }
    if (filters.status) {
      conditions.push(`status = $${paramIndex++}`);
      params.push(filters.status);
    }
    if (filters.isFree !== undefined) {
      conditions.push(`is_free = $${paramIndex++}`);
      params.push(filters.isFree);
    }
    if (filters.instructorId) {
      conditions.push(`instructor_id = $${paramIndex++}`);
      params.push(filters.instructorId);
    }
    if (filters.search) {
      conditions.push(`(title ILIKE $${paramIndex} OR description ILIKE $${paramIndex})`);
      params.push(`%${filters.search}%`);
      paramIndex++;
    }
    if (filters.minRating) {
      conditions.push(`average_rating >= $${paramIndex++}`);
      params.push(filters.minRating);
    }

    const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
    const allowedSortColumns = ['created_at', 'title', 'price', 'average_rating', 'enrolled_count'];
    const safeSort = allowedSortColumns.includes(sortBy) ? sortBy : 'created_at';
    const safeSortOrder = sortOrder === 'asc' ? 'ASC' : 'DESC';

    const countResult = await db.query<{ count: string }>(
      `SELECT COUNT(*) as count FROM education.courses ${whereClause}`,
      params
    );
    const total = parseInt(countResult.rows[0].count, 10);

    params.push(pageSize, offset);
    const dataResult = await db.query<Record<string, unknown>>(
      `SELECT * FROM education.courses ${whereClause}
       ORDER BY ${safeSort} ${safeSortOrder}
       LIMIT $${paramIndex++} OFFSET $${paramIndex}`,
      params
    );

    return {
      data: dataResult.rows.map(transformCourse),
      total,
      page,
      pageSize,
      totalPages: Math.ceil(total / pageSize),
    };
  }

  async getCourseById(id: string): Promise<Course | null> {
    const result = await db.query<Record<string, unknown>>(
      `SELECT * FROM education.courses WHERE id = $1`,
      [id]
    );
    if (result.rows.length === 0) return null;
    return transformCourse(result.rows[0]);
  }

  async getCourseBySlug(slug: string): Promise<Course | null> {
    const result = await db.query<Record<string, unknown>>(
      `SELECT * FROM education.courses WHERE slug = $1`,
      [slug]
    );
    if (result.rows.length === 0) return null;
    return transformCourse(result.rows[0]);
  }

  async getCourseWithDetails(id: string): Promise<CourseWithDetails | null> {
    const course = await this.getCourseById(id);
    if (!course) return null;

    // Get category
    let category: Category | undefined;
    if (course.categoryId) {
      category = (await this.getCategoryById(course.categoryId)) || undefined;
    }

    // Get modules with lessons
    const modules = await this.getCourseModules(id);

    return {
      ...course,
      category,
      modules,
    };
  }

  async createCourse(input: CreateCourseInput): Promise<Course> {
    const slug = input.slug || generateSlug(input.title);

    const result = await db.query<Record<string, unknown>>(
      `INSERT INTO education.courses (
        title, slug, description, short_description, thumbnail_url, preview_video_url,
        category_id, level, tags, is_free, price, currency, requires_subscription,
        min_subscription_tier, duration_minutes, instructor_id, status
      ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, 'draft')
      RETURNING *`,
      [
        input.title,
        slug,
        input.description,
        input.shortDescription,
        input.thumbnailUrl,
        input.previewVideoUrl,
        input.categoryId,
        input.level || 'beginner',
        input.tags || [],
        input.isFree ?? false,
        input.price || 0,
        input.currency || 'USD',
        input.requiresSubscription ?? false,
        input.minSubscriptionTier,
        input.durationMinutes,
        input.instructorId,
      ]
    );

    logger.info('[CourseService] Course created:', { courseId: result.rows[0].id, title: input.title });
    return transformCourse(result.rows[0]);
  }

  async updateCourse(id: string, input: UpdateCourseInput): Promise<Course | null> {
    const updates: string[] = [];
    const params: (string | number | boolean | string[] | null)[] = [];
    let paramIndex = 1;

    if (input.title !== undefined) {
      updates.push(`title = $${paramIndex++}`);
      params.push(input.title);
    }
    if (input.slug !== undefined) {
      updates.push(`slug = $${paramIndex++}`);
      params.push(input.slug);
    }
    if (input.description !== undefined) {
      updates.push(`description = $${paramIndex++}`);
      params.push(input.description);
    }
    if (input.shortDescription !== undefined) {
      updates.push(`short_description = $${paramIndex++}`);
      params.push(input.shortDescription);
    }
    if (input.thumbnailUrl !== undefined) {
      updates.push(`thumbnail_url = $${paramIndex++}`);
      params.push(input.thumbnailUrl);
    }
    if (input.categoryId !== undefined) {
      updates.push(`category_id = $${paramIndex++}`);
      params.push(input.categoryId);
    }
    if (input.level !== undefined) {
      updates.push(`level = $${paramIndex++}`);
      params.push(input.level);
    }
    if (input.tags !== undefined) {
      updates.push(`tags = $${paramIndex++}`);
      params.push(input.tags);
    }
    if (input.isFree !== undefined) {
      updates.push(`is_free = $${paramIndex++}`);
      params.push(input.isFree);
    }
    if (input.price !== undefined) {
      updates.push(`price = $${paramIndex++}`);
      params.push(input.price);
    }
    if (input.status !== undefined) {
      updates.push(`status = $${paramIndex++}`);
      params.push(input.status);
      if (input.status === 'published') {
        updates.push(`published_at = CURRENT_TIMESTAMP`);
      }
    }

    if (updates.length === 0) return this.getCourseById(id);

    params.push(id);
    const result = await db.query<Record<string, unknown>>(
      `UPDATE education.courses SET ${updates.join(', ')} WHERE id = $${paramIndex} RETURNING *`,
      params
    );

    if (result.rows.length === 0) return null;
    logger.info('[CourseService] Course updated:', { courseId: id });
    return transformCourse(result.rows[0]);
  }

  async deleteCourse(id: string): Promise<boolean> {
    const result = await db.query(`DELETE FROM education.courses WHERE id = $1`, [id]);
    logger.info('[CourseService] Course deleted:', { courseId: id });
    return (result.rowCount ?? 0) > 0;
  }

  async publishCourse(id: string): Promise<Course | null> {
    return this.updateCourse(id, { status: 'published' });
  }

  async archiveCourse(id: string): Promise<Course | null> {
    return this.updateCourse(id, { status: 'archived' });
  }

  // ==========================================================================
  // Modules
  // ==========================================================================

  async getCourseModules(courseId: string): Promise<ModuleWithLessons[]> {
    const modulesResult = await db.query<Record<string, unknown>>(
      `SELECT * FROM education.modules WHERE course_id = $1 ORDER BY sort_order`,
      [courseId]
    );

    const modules: ModuleWithLessons[] = [];
    for (const row of modulesResult.rows) {
      const lessons = await this.getModuleLessons(row.id as string);
      modules.push({
        id: row.id as string,
        courseId: row.course_id as string,
        title: row.title as string,
        description: row.description as string | undefined,
        sortOrder: row.sort_order as number,
        unlockAfterModuleId: row.unlock_after_module_id as string | undefined,
        createdAt: new Date(row.created_at as string),
        updatedAt: new Date(row.updated_at as string),
        lessons,
      });
    }

    return modules;
  }

  async getModuleById(id: string): Promise<Module | null> {
    const result = await db.query<Record<string, unknown>>(
      `SELECT * FROM education.modules WHERE id = $1`,
      [id]
    );
    if (result.rows.length === 0) return null;
    const row = result.rows[0];
    return {
      id: row.id as string,
      courseId: row.course_id as string,
      title: row.title as string,
      description: row.description as string | undefined,
      sortOrder: row.sort_order as number,
      unlockAfterModuleId: row.unlock_after_module_id as string | undefined,
      createdAt: new Date(row.created_at as string),
      updatedAt: new Date(row.updated_at as string),
    };
  }

  async createModule(input: CreateModuleInput): Promise<Module> {
    const result = await db.query<Record<string, unknown>>(
      `INSERT INTO education.modules (course_id, title, description, sort_order, unlock_after_module_id)
       VALUES ($1, $2, $3, $4, $5)
       RETURNING *`,
      [input.courseId, input.title, input.description, input.sortOrder || 0, input.unlockAfterModuleId]
    );
    const row = result.rows[0];
    return {
      id: row.id as string,
      courseId: row.course_id as string,
      title: row.title as string,
      description: row.description as string | undefined,
      sortOrder: row.sort_order as number,
      unlockAfterModuleId: row.unlock_after_module_id as string | undefined,
      createdAt: new Date(row.created_at as string),
      updatedAt: new Date(row.updated_at as string),
    };
  }

  async deleteModule(id: string): Promise<boolean> {
    const result = await db.query(`DELETE FROM education.modules WHERE id = $1`, [id]);
    return (result.rowCount ?? 0) > 0;
  }

  // ==========================================================================
  // Lessons
  // ==========================================================================

  async getModuleLessons(moduleId: string): Promise<Lesson[]> {
    const result = await db.query<Record<string, unknown>>(
      `SELECT * FROM education.lessons WHERE module_id = $1 ORDER BY sort_order`,
      [moduleId]
    );
    return result.rows.map(transformLesson);
  }

  async getLessonById(id: string): Promise<Lesson | null> {
    const result = await db.query<Record<string, unknown>>(
      `SELECT * FROM education.lessons WHERE id = $1`,
      [id]
    );
    if (result.rows.length === 0) return null;
    return transformLesson(result.rows[0]);
  }

  async createLesson(input: CreateLessonInput): Promise<Lesson> {
    const slug = input.slug || generateSlug(input.title);

    const result = await db.query<Record<string, unknown>>(
      `INSERT INTO education.lessons (
        module_id, course_id, title, slug, content_type, video_url,
        video_duration_seconds, video_provider, content_markdown,
        resources, sort_order, is_preview
      ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
      RETURNING *`,
      [
        input.moduleId,
        input.courseId,
        input.title,
        slug,
        input.contentType || 'video',
        input.videoUrl,
        input.videoDurationSeconds,
        input.videoProvider,
        input.contentMarkdown,
        JSON.stringify(input.resources || []),
        input.sortOrder || 0,
        input.isPreview ?? false,
      ]
    );

    // Update course lessons count
    await db.query(
      `UPDATE education.courses SET lessons_count = lessons_count + 1 WHERE id = $1`,
      [input.courseId]
    );

    return transformLesson(result.rows[0]);
  }

  async deleteLesson(id: string): Promise<boolean> {
    // Get lesson to update course count
    const lesson = await this.getLessonById(id);
    if (!lesson) return false;

    const result = await db.query(`DELETE FROM education.lessons WHERE id = $1`, [id]);

    if ((result.rowCount ?? 0) > 0) {
      await db.query(
        `UPDATE education.courses SET lessons_count = GREATEST(lessons_count - 1, 0) WHERE id = $1`,
        [lesson.courseId]
      );
    }

    return (result.rowCount ?? 0) > 0;
  }

  // ==========================================================================
  // Statistics
  // ==========================================================================

  async updateCourseStats(courseId: string): Promise<void> {
    await db.query(
      `UPDATE education.courses
       SET lessons_count = (SELECT COUNT(*) FROM education.lessons WHERE course_id = $1),
           duration_minutes = (SELECT COALESCE(SUM(video_duration_seconds) / 60, 0) FROM education.lessons WHERE course_id = $1)
       WHERE id = $1`,
      [courseId]
    );
  }

  async getPopularCourses(limit: number = 10): Promise<Course[]> {
    const result = await db.query<Record<string, unknown>>(
      `SELECT * FROM education.courses
       WHERE status = 'published'
       ORDER BY enrolled_count DESC, average_rating DESC
       LIMIT $1`,
      [limit]
    );
    return result.rows.map(transformCourse);
  }

  async getNewCourses(limit: number = 10): Promise<Course[]> {
    const result = await db.query<Record<string, unknown>>(
      `SELECT * FROM education.courses
       WHERE status = 'published'
       ORDER BY published_at DESC
       LIMIT $1`,
      [limit]
    );
    return result.rows.map(transformCourse);
  }
}

export const courseService = new CourseService();
