import { Injectable, Logger } from '@nestjs/common';
import { TodoistConnector } from './todoist.connector';
import {
  GetTasksQueryDto,
  CreateTaskDto,
  UpdateTaskDto,
  CreateProjectDto,
  UpdateProjectDto,
  TaskResponseDto,
  ProjectResponseDto,
  SectionResponseDto,
  LabelResponseDto,
} from './dto';
import { ConnectionTestResult } from '../base-connector';
import {
  TodoistTask,
  TodoistProject,
  TodoistSection,
  TodoistLabel,
} from './todoist.types';

/**
 * Service layer for Todoist integration
 *
 * Provides business logic and data transformation for Todoist API calls.
 */
@Injectable()
export class TodoistService {
  private readonly logger = new Logger(TodoistService.name);

  constructor(private readonly connector: TodoistConnector) {}

  /**
   * Test the connection to Todoist
   */
  async testConnection(): Promise<ConnectionTestResult> {
    this.logger.log('Testing Todoist connection');
    return this.connector.testConnection();
  }

  /**
   * Check if Todoist integration is configured
   */
  isConfigured(): boolean {
    return this.connector.isConfigured();
  }

  // ===========================================================================
  // Tasks
  // ===========================================================================

  /**
   * Get list of tasks
   */
  async getTasks(query: GetTasksQueryDto): Promise<TaskResponseDto[]> {
    this.logger.log(`Fetching tasks with query: ${JSON.stringify(query)}`);

    const tasks = await this.connector.getTasks({
      project_id: query.projectId,
      section_id: query.sectionId,
      label: query.label,
      filter: query.filter,
    });

    return tasks.map(this.mapTaskToDto);
  }

  /**
   * Get a single task by ID
   */
  async getTask(taskId: string): Promise<TaskResponseDto> {
    this.logger.log(`Fetching task ${taskId}`);

    const task = await this.connector.getTask(taskId);
    return this.mapTaskToDto(task);
  }

  /**
   * Create a new task
   */
  async createTask(dto: CreateTaskDto): Promise<TaskResponseDto> {
    this.logger.log(`Creating task: ${dto.content}`);

    const task = await this.connector.createTask({
      content: dto.content,
      description: dto.description,
      project_id: dto.projectId,
      section_id: dto.sectionId,
      parent_id: dto.parentId,
      labels: dto.labels,
      priority: dto.priority,
      due_string: dto.dueString,
      due_date: dto.dueDate,
      assignee_id: dto.assigneeId,
      duration: dto.duration,
      duration_unit: dto.durationUnit,
    });

    return this.mapTaskToDto(task);
  }

  /**
   * Update a task
   */
  async updateTask(
    taskId: string,
    dto: UpdateTaskDto,
  ): Promise<TaskResponseDto> {
    this.logger.log(`Updating task ${taskId}`);

    const task = await this.connector.updateTask(taskId, {
      content: dto.content,
      description: dto.description,
      labels: dto.labels,
      priority: dto.priority,
      due_string: dto.dueString,
      due_date: dto.dueDate,
      assignee_id: dto.assigneeId,
    });

    return this.mapTaskToDto(task);
  }

  /**
   * Complete a task
   */
  async completeTask(taskId: string): Promise<{ success: boolean }> {
    this.logger.log(`Completing task ${taskId}`);

    await this.connector.completeTask(taskId);
    return { success: true };
  }

  /**
   * Reopen a task
   */
  async reopenTask(taskId: string): Promise<{ success: boolean }> {
    this.logger.log(`Reopening task ${taskId}`);

    await this.connector.reopenTask(taskId);
    return { success: true };
  }

  /**
   * Delete a task
   */
  async deleteTask(taskId: string): Promise<{ success: boolean }> {
    this.logger.log(`Deleting task ${taskId}`);

    await this.connector.deleteTask(taskId);
    return { success: true };
  }

  // ===========================================================================
  // Projects
  // ===========================================================================

  /**
   * Get all projects
   */
  async getProjects(): Promise<ProjectResponseDto[]> {
    this.logger.log('Fetching projects');

    const projects = await this.connector.getProjects();
    return projects.map(this.mapProjectToDto);
  }

  /**
   * Get a single project by ID
   */
  async getProject(projectId: string): Promise<ProjectResponseDto> {
    this.logger.log(`Fetching project ${projectId}`);

    const project = await this.connector.getProject(projectId);
    return this.mapProjectToDto(project);
  }

  /**
   * Create a new project
   */
  async createProject(dto: CreateProjectDto): Promise<ProjectResponseDto> {
    this.logger.log(`Creating project: ${dto.name}`);

    const project = await this.connector.createProject({
      name: dto.name,
      parent_id: dto.parentId,
      color: dto.color,
      is_favorite: dto.isFavorite,
      view_style: dto.viewStyle,
    });

    return this.mapProjectToDto(project);
  }

  /**
   * Update a project
   */
  async updateProject(
    projectId: string,
    dto: UpdateProjectDto,
  ): Promise<ProjectResponseDto> {
    this.logger.log(`Updating project ${projectId}`);

    const project = await this.connector.updateProject(projectId, {
      name: dto.name,
      color: dto.color,
      is_favorite: dto.isFavorite,
      view_style: dto.viewStyle,
    });

    return this.mapProjectToDto(project);
  }

  /**
   * Delete a project
   */
  async deleteProject(projectId: string): Promise<{ success: boolean }> {
    this.logger.log(`Deleting project ${projectId}`);

    await this.connector.deleteProject(projectId);
    return { success: true };
  }

  // ===========================================================================
  // Sections
  // ===========================================================================

  /**
   * Get sections (optionally filtered by project)
   */
  async getSections(projectId?: string): Promise<SectionResponseDto[]> {
    this.logger.log(`Fetching sections${projectId ? ` for project ${projectId}` : ''}`);

    const sections = await this.connector.getSections(projectId);
    return sections.map(this.mapSectionToDto);
  }

  // ===========================================================================
  // Labels
  // ===========================================================================

  /**
   * Get all labels
   */
  async getLabels(): Promise<LabelResponseDto[]> {
    this.logger.log('Fetching labels');

    const labels = await this.connector.getLabels();
    return labels.map(this.mapLabelToDto);
  }

  // ===========================================================================
  // Mappers
  // ===========================================================================

  /**
   * Map Todoist task to response DTO
   */
  private mapTaskToDto = (task: TodoistTask): TaskResponseDto => ({
    id: task.id,
    content: task.content,
    description: task.description,
    projectId: task.project_id,
    sectionId: task.section_id || undefined,
    parentId: task.parent_id || undefined,
    isCompleted: task.is_completed,
    labels: task.labels,
    priority: task.priority,
    commentCount: task.comment_count,
    createdAt: task.created_at,
    due: task.due
      ? {
          date: task.due.date,
          string: task.due.string,
          isRecurring: task.due.is_recurring,
          datetime: task.due.datetime,
        }
      : undefined,
    url: task.url,
  });

  /**
   * Map Todoist project to response DTO
   */
  private mapProjectToDto = (project: TodoistProject): ProjectResponseDto => ({
    id: project.id,
    name: project.name,
    color: project.color,
    parentId: project.parent_id || undefined,
    order: project.order,
    commentCount: project.comment_count,
    isShared: project.is_shared,
    isFavorite: project.is_favorite,
    isInboxProject: project.is_inbox_project,
    viewStyle: project.view_style,
    url: project.url,
  });

  /**
   * Map Todoist section to response DTO
   */
  private mapSectionToDto = (section: TodoistSection): SectionResponseDto => ({
    id: section.id,
    projectId: section.project_id,
    name: section.name,
    order: section.order,
  });

  /**
   * Map Todoist label to response DTO
   */
  private mapLabelToDto = (label: TodoistLabel): LabelResponseDto => ({
    id: label.id,
    name: label.name,
    color: label.color,
    order: label.order,
    isFavorite: label.is_favorite,
  });
}
