/**
 * Todo Service
 * Handles todo CRUD operations with three-state workflow
 */

import { prisma } from '../lib/prisma';
import { Prisma } from '@prisma/client';

export type TodoStatus = 'pending' | 'completed' | 'confirmed';
export type TodoPriority = 'low' | 'medium' | 'high' | 'urgent';

export interface CreateTodoInput {
  user_id: string;
  title: string;
  description?: string | null;
  priority?: TodoPriority;
  status?: TodoStatus;
  due_date?: Date | null;
  category_id?: string | null;
  document_id?: string | null;
}

export interface UpdateTodoInput {
  title?: string;
  description?: string | null;
  priority?: TodoPriority;
  status?: TodoStatus;
  due_date?: Date | null;
  category_id?: string | null;
  document_id?: string | null;
}

export interface FindTodoOptions {
  status?: TodoStatus;
  priority?: TodoPriority;
  category_id?: string;
  document_id?: string;
  page?: number;
  limit?: number;
  overdue?: boolean;
}

export class TodoService {
  /**
   * Create a new todo
   */
  static async create(input: CreateTodoInput) {
    // Validate title is not empty
    if (!input.title || input.title.trim().length === 0) {
      throw new Error('Todo title cannot be empty');
    }

    // Validate priority if provided
    const validPriorities: TodoPriority[] = ['low', 'medium', 'high', 'urgent'];
    if (input.priority && !validPriorities.includes(input.priority)) {
      throw new Error(`Invalid priority. Must be one of: ${validPriorities.join(', ')}`);
    }

    // Validate status if provided
    const validStatuses: TodoStatus[] = ['pending', 'completed', 'confirmed'];
    if (input.status && !validStatuses.includes(input.status)) {
      throw new Error(`Invalid status. Must be one of: ${validStatuses.join(', ')}`);
    }

    return prisma.todo.create({
      data: {
        user_id: input.user_id,
        title: input.title,
        description: input.description ?? null,
        priority: input.priority ?? 'medium',
        status: input.status ?? 'pending',
        due_date: input.due_date ?? null,
        category_id: input.category_id ?? null,
        document_id: input.document_id ?? null,
      },
    });
  }

  /**
   * Find todo by ID (ensuring user owns it)
   */
  static async findById(id: string, userId: string) {
    return prisma.todo.findFirst({
      where: {
        id,
        user_id: userId,
      },
    });
  }

  /**
   * Update todo
   */
  static async update(id: string, userId: string, input: UpdateTodoInput) {
    // Verify todo exists and user owns it
    const existing = await this.findById(id, userId);
    if (!existing) {
      throw new Error('Todo not found or access denied');
    }

    const updateData: Prisma.TodoUpdateInput = {};

    if (input.title !== undefined) {
      updateData.title = input.title;
    }
    if (input.description !== undefined) {
      updateData.description = input.description;
    }
    if (input.priority !== undefined) {
      updateData.priority = input.priority;
    }
    if (input.category_id !== undefined) {
      updateData.category = input.category_id ? { connect: { id: input.category_id } } : { disconnect: true };
    }
    if (input.document_id !== undefined) {
      updateData.document = input.document_id ? { connect: { id: input.document_id } } : { disconnect: true };
    }
    if (input.due_date !== undefined) {
      updateData.due_date = input.due_date;
    }

    // Handle status transitions with timestamps
    if (input.status !== undefined) {
      updateData.status = input.status;

      if (input.status === 'completed' && existing.status !== 'completed' && existing.status !== 'confirmed') {
        updateData.completed_at = new Date();
      } else if (input.status === 'confirmed') {
        updateData.confirmed_at = new Date();
        // Ensure completed_at is set if not already
        if (!existing.completed_at) {
          updateData.completed_at = new Date();
        }
      } else if (input.status === 'pending') {
        // Clear timestamps when reverting to pending
        updateData.completed_at = null;
        updateData.confirmed_at = null;
      }
    }

    return prisma.todo.update({
      where: { id },
      data: updateData,
    });
  }

  /**
   * Delete todo
   */
  static async delete(id: string, userId: string) {
    // Verify todo exists and user owns it
    const existing = await this.findById(id, userId);
    if (!existing) {
      throw new Error('Todo not found or access denied');
    }

    await prisma.todo.delete({
      where: { id },
    });
  }

  /**
   * Find all todos for a user with filters
   */
  static async findByUser(userId: string, options: FindTodoOptions = {}) {
    const where: Prisma.TodoWhereInput = {
      user_id: userId,
    };

    if (options.status) {
      where.status = options.status;
    }

    if (options.priority) {
      where.priority = options.priority;
    }

    if (options.category_id) {
      where.category_id = options.category_id;
    }

    if (options.document_id) {
      where.document_id = options.document_id;
    }

    if (options.overdue) {
      where.due_date = {
        lt: new Date(),
      };
      // Only pending todos can be overdue
      where.status = 'pending';
    }

    const page = options.page ?? 1;
    const limit = options.limit ?? 50;
    const skip = (page - 1) * limit;

    // Fetch more than needed to allow for post-sorting
    const fetchLimit = limit * 2;

    let todos = await prisma.todo.findMany({
      where,
      orderBy: [{ created_at: 'desc' }],
      skip: 0,
      take: fetchLimit,
    });

    // Sort by priority in application layer
    const priorityOrder: Record<TodoPriority, number> = { urgent: 0, high: 1, medium: 2, low: 3 };
    todos = todos.sort((a, b) => {
      const priorityDiff = priorityOrder[a.priority as TodoPriority] - priorityOrder[b.priority as TodoPriority];
      if (priorityDiff !== 0) return priorityDiff;
      // If same priority, sort by created date
      return b.created_at.getTime() - a.created_at.getTime();
    });

    // Apply pagination after sorting
    return todos.slice(skip, skip + limit);
  }

  /**
   * Get pending todos
   */
  static async getPendingTodos(userId: string, limit = 50) {
    return prisma.todo.findMany({
      where: {
        user_id: userId,
        status: 'pending',
      },
      orderBy: [{ created_at: 'desc' }],
      take: limit,
    });
  }

  /**
   * Get completed todos
   */
  static async getCompletedTodos(userId: string, limit = 50) {
    return prisma.todo.findMany({
      where: {
        user_id: userId,
        status: 'completed',
      },
      orderBy: [{ completed_at: 'desc' }],
      take: limit,
    });
  }

  /**
   * Get confirmed todos
   */
  static async getConfirmedTodos(userId: string, limit = 50) {
    return prisma.todo.findMany({
      where: {
        user_id: userId,
        status: 'confirmed',
      },
      orderBy: [{ confirmed_at: 'desc' }],
      take: limit,
    });
  }
}
