package com.ikon.projectmanagement.service.impl;

import com.ikon.projectmanagement.dto.request.ScheduleDependencyDto;
import com.ikon.projectmanagement.dto.request.ScheduleGroupDto;
import com.ikon.projectmanagement.dto.request.ScheduleRequestDto;
import com.ikon.projectmanagement.dto.request.ScheduleTaskDto;
import com.ikon.projectmanagement.dto.response.ScheduleResponseDto;
import com.ikon.projectmanagement.entity.ProductOfProject;
import com.ikon.projectmanagement.entity.ProjectSchedule;
import com.ikon.projectmanagement.mapper.ScheduleMapper;
import com.ikon.projectmanagement.repository.ProductOfProjectRepository;
import com.ikon.projectmanagement.repository.ProjectScheduleRepository;
import com.ikon.projectmanagement.service.ScheduleService;
import com.ikon.webservice.WebService;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

@Service
@RequiredArgsConstructor
@Slf4j
public class ScheduleServiceImpl extends WebService implements ScheduleService {

    private final ProjectScheduleRepository scheduleRepository;
    private final ProductOfProjectRepository productOfProjectRepository;
    private final ScheduleMapper scheduleMapper;

    @PersistenceContext
    private EntityManager entityManager;

    private ProjectSchedule getOrCreateSchedule(UUID projectIdentifier) {
        UUID accountId = getActiveAccountId();
        return scheduleRepository
                .findByAccountIdAndProjectIdentifier(accountId, projectIdentifier)
                .orElseGet(() -> {
                    UUID productId = productOfProjectRepository
                            .findByAccountIdAndProjectIdentifier(accountId, projectIdentifier)
                            .stream().findFirst()
                            .map(ProductOfProject::getProductIdentifier)
                            .orElse(null);
                    ProjectSchedule newSchedule = ProjectSchedule.builder()
                            .projectIdentifier(projectIdentifier)
                            .productIdentifier(productId)
                            .accountId(accountId)
                            .build();
                    return scheduleRepository.save(newSchedule);
                });
    }

    @Override
    @Transactional
    public ScheduleResponseDto saveSchedule(UUID projectIdentifier, ScheduleRequestDto dto, String accessToken) {
        log.debug("saveSchedule called for project: {}", projectIdentifier);
        ProjectSchedule schedule = getOrCreateSchedule(projectIdentifier);

        // Clear existing data — orphanRemoval = true handles DB deletion
        schedule.getTasks().clear();
        schedule.getDependencies().clear();
        schedule.getGroups().clear();

        // Flush DELETEs first to avoid PK conflicts when re-inserting same IDs
        entityManager.flush();

        // Repopulate tasks
        List<ScheduleTaskDto> tasks = dto.getTask();
        if (tasks != null) {
            for (ScheduleTaskDto taskDto : tasks) {
                schedule.getTasks().add(scheduleMapper.toTaskEntity(taskDto, schedule));
            }
        }

        // Repopulate dependencies
        List<ScheduleDependencyDto> dependencies = dto.getDependency();
        if (dependencies != null) {
            for (ScheduleDependencyDto depDto : dependencies) {
                schedule.getDependencies().add(scheduleMapper.toDependencyEntity(depDto, schedule));
            }
        }

        // Repopulate groups
        Map<String, ScheduleGroupDto> groups = dto.getGroup();
        if (groups != null) {
            for (Map.Entry<String, ScheduleGroupDto> entry : groups.entrySet()) {
                schedule.getGroups().add(scheduleMapper.toGroupEntity(entry.getKey(), entry.getValue(), schedule));
            }
        }

        ProjectSchedule saved = scheduleRepository.save(schedule);
        log.debug("Schedule saved for project {}: {} tasks, {} dependencies, {} groups",
                projectIdentifier,
                saved.getTasks().size(),
                saved.getDependencies().size(),
                saved.getGroups().size());

        return scheduleMapper.toResponse(saved);
    }

    @Override
    @Transactional(readOnly = true)
    public ScheduleResponseDto getSchedule(UUID projectIdentifier, String accessToken) {
        UUID accountId = getActiveAccountId();
        return scheduleRepository
                .findByAccountIdAndProjectIdentifier(accountId, projectIdentifier)
                .map(scheduleMapper::toResponse)
                .orElse(new ScheduleResponseDto(projectIdentifier, null, new ArrayList<>(), new ArrayList<>(), new LinkedHashMap<>()));
    }

    @Override
    @Transactional(readOnly = true)
    public List<ScheduleResponseDto> getAllSchedules() {
        UUID accountId = getActiveAccountId();
        return scheduleRepository
                .findAllByAccountId(accountId)
                .stream()
                .map(scheduleMapper::toResponse)
                .toList();
    }
}
