import { Injectable, NotFoundException } from "@nestjs/common";
import { PrismaService } from "../prisma/prisma.service";
import { AgentTaskStatus, AgentTaskPriority, Prisma } from "@prisma/client";
import type { CreateAgentTaskDto, UpdateAgentTaskDto, QueryAgentTasksDto } from "./dto";

/**
 * Service for managing agent tasks
 */
@Injectable()
export class AgentTasksService {
  constructor(private readonly prisma: PrismaService) {}

  /**
   * Create a new agent task
   */
  async create(workspaceId: string, userId: string, createAgentTaskDto: CreateAgentTaskDto) {
    // Build the create input, handling optional fields properly for exactOptionalPropertyTypes
    const createInput: Prisma.AgentTaskUncheckedCreateInput = {
      title: createAgentTaskDto.title,
      workspaceId,
      createdById: userId,
      status: createAgentTaskDto.status ?? AgentTaskStatus.PENDING,
      priority: createAgentTaskDto.priority ?? AgentTaskPriority.MEDIUM,
      agentType: createAgentTaskDto.agentType,
      agentConfig: (createAgentTaskDto.agentConfig ?? {}) as Prisma.InputJsonValue,
    };

    // Add optional fields only if they exist
    if (createAgentTaskDto.description) createInput.description = createAgentTaskDto.description;
    if (createAgentTaskDto.result)
      createInput.result = createAgentTaskDto.result as Prisma.InputJsonValue;
    if (createAgentTaskDto.error) createInput.error = createAgentTaskDto.error;

    // Set startedAt if status is RUNNING
    if (createInput.status === AgentTaskStatus.RUNNING) {
      createInput.startedAt = new Date();
    }

    // Set completedAt if status is COMPLETED or FAILED
    if (
      createInput.status === AgentTaskStatus.COMPLETED ||
      createInput.status === AgentTaskStatus.FAILED
    ) {
      createInput.completedAt = new Date();
      createInput.startedAt ??= new Date();
    }

    const agentTask = await this.prisma.agentTask.create({
      data: createInput,
      include: {
        createdBy: {
          select: { id: true, name: true, email: true },
        },
      },
    });

    return agentTask;
  }

  /**
   * Get paginated agent tasks with filters
   */
  async findAll(query: QueryAgentTasksDto) {
    const page = query.page ?? 1;
    const limit = query.limit ?? 50;
    const skip = (page - 1) * limit;

    // Build where clause
    const where: Prisma.AgentTaskWhereInput = {};

    if (query.workspaceId) {
      where.workspaceId = query.workspaceId;
    }

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

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

    if (query.agentType) {
      where.agentType = query.agentType;
    }

    if (query.createdById) {
      where.createdById = query.createdById;
    }

    // Execute queries in parallel
    const [data, total] = await Promise.all([
      this.prisma.agentTask.findMany({
        where,
        include: {
          createdBy: {
            select: { id: true, name: true, email: true },
          },
        },
        orderBy: {
          createdAt: "desc",
        },
        skip,
        take: limit,
      }),
      this.prisma.agentTask.count({ where }),
    ]);

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

  /**
   * Get a single agent task by ID
   */
  async findOne(id: string, workspaceId: string) {
    const agentTask = await this.prisma.agentTask.findUnique({
      where: {
        id,
        workspaceId,
      },
      include: {
        createdBy: {
          select: { id: true, name: true, email: true },
        },
      },
    });

    if (!agentTask) {
      throw new NotFoundException(`Agent task with ID ${id} not found`);
    }

    return agentTask;
  }

  /**
   * Update an agent task
   */
  async update(id: string, workspaceId: string, updateAgentTaskDto: UpdateAgentTaskDto) {
    // Verify agent task exists
    const existingTask = await this.prisma.agentTask.findUnique({
      where: { id, workspaceId },
    });

    if (!existingTask) {
      throw new NotFoundException(`Agent task with ID ${id} not found`);
    }

    const data: Prisma.AgentTaskUpdateInput = {};

    // Only include fields that are actually being updated
    if (updateAgentTaskDto.title !== undefined) data.title = updateAgentTaskDto.title;
    if (updateAgentTaskDto.description !== undefined)
      data.description = updateAgentTaskDto.description;
    if (updateAgentTaskDto.status !== undefined) data.status = updateAgentTaskDto.status;
    if (updateAgentTaskDto.priority !== undefined) data.priority = updateAgentTaskDto.priority;
    if (updateAgentTaskDto.agentType !== undefined) data.agentType = updateAgentTaskDto.agentType;
    if (updateAgentTaskDto.error !== undefined) data.error = updateAgentTaskDto.error;

    if (updateAgentTaskDto.agentConfig !== undefined) {
      data.agentConfig = updateAgentTaskDto.agentConfig as Prisma.InputJsonValue;
    }

    if (updateAgentTaskDto.result !== undefined) {
      data.result =
        updateAgentTaskDto.result === null
          ? Prisma.JsonNull
          : (updateAgentTaskDto.result as Prisma.InputJsonValue);
    }

    // Handle startedAt based on status changes
    if (updateAgentTaskDto.status) {
      if (
        updateAgentTaskDto.status === AgentTaskStatus.RUNNING &&
        existingTask.status === AgentTaskStatus.PENDING &&
        !existingTask.startedAt
      ) {
        data.startedAt = new Date();
      }

      // Handle completedAt based on status changes
      if (
        (updateAgentTaskDto.status === AgentTaskStatus.COMPLETED ||
          updateAgentTaskDto.status === AgentTaskStatus.FAILED) &&
        existingTask.status !== AgentTaskStatus.COMPLETED &&
        existingTask.status !== AgentTaskStatus.FAILED
      ) {
        data.completedAt = new Date();
        if (!existingTask.startedAt) {
          data.startedAt = new Date();
        }
      }
    }

    const agentTask = await this.prisma.agentTask.update({
      where: {
        id,
        workspaceId,
      },
      data,
      include: {
        createdBy: {
          select: { id: true, name: true, email: true },
        },
      },
    });

    return agentTask;
  }

  /**
   * Delete an agent task
   */
  async remove(id: string, workspaceId: string) {
    // Verify agent task exists
    const agentTask = await this.prisma.agentTask.findUnique({
      where: { id, workspaceId },
    });

    if (!agentTask) {
      throw new NotFoundException(`Agent task with ID ${id} not found`);
    }

    await this.prisma.agentTask.delete({
      where: {
        id,
        workspaceId,
      },
    });

    return { message: "Agent task deleted successfully" };
  }
}
