package com.magistr.app.service;

import com.magistr.app.dto.CreateSubjectRequest;
import com.magistr.app.model.Subject;
import com.magistr.app.repository.SubjectRepository;
import org.springframework.dao.DataIntegrityViolationException;
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.Locale;
import java.util.Map;
import java.util.Objects;

@Service
public class SubjectImportService {

    private static final int MAX_NAME_LENGTH = 200;
    private static final int MAX_CODE_LENGTH = 20;

    private final SubjectRepository subjectRepository;

    public SubjectImportService(SubjectRepository subjectRepository) {
        this.subjectRepository = subjectRepository;
    }

    @Transactional
    public List<Subject> importSubjects(List<CreateSubjectRequest> requests, Long departmentId) {
        if (requests == null || requests.isEmpty()) {
            throw new IllegalArgumentException("Передайте список дисциплин для загрузки");
        }
        if (departmentId == null || departmentId <= 0) {
            throw new IllegalArgumentException("Кафедра обязательна");
        }

        List<SubjectCandidate> candidates = normalizeCandidates(requests);
        Map<String, Subject> existingByCanonicalName = new LinkedHashMap<>();
        for (SubjectCandidate candidate : candidates) {
            subjectRepository.findByNameIgnoreCase(candidate.name()).ifPresent(existing -> {
                if (!Objects.equals(existing.getDepartmentId(), departmentId)) {
                    throw new ScheduleConflictException(
                            "Дисциплина «" + candidate.name() + "» уже принадлежит другой кафедре"
                    );
                }
                existingByCanonicalName.put(candidate.canonicalName(), existing);
            });
        }

        List<Subject> subjects = new ArrayList<>(candidates.size());
        for (SubjectCandidate candidate : candidates) {
            Subject subject = existingByCanonicalName.getOrDefault(
                    candidate.canonicalName(),
                    new Subject()
            );
            subject.setName(candidate.name());
            subject.setCode(candidate.code());
            subject.setDepartmentId(departmentId);
            if (subject.isArchivedRecord()) {
                subject.restore();
            }
            subjects.add(subject);
        }

        try {
            return subjectRepository.saveAllAndFlush(subjects);
        } catch (DataIntegrityViolationException exception) {
            throw new ScheduleConflictException(
                    "Дисциплина с таким названием уже существует"
            );
        }
    }

    private List<SubjectCandidate> normalizeCandidates(List<CreateSubjectRequest> requests) {
        Map<String, SubjectCandidate> unique = new LinkedHashMap<>();
        for (CreateSubjectRequest request : requests) {
            if (request == null || request.getName() == null || request.getName().isBlank()) {
                continue;
            }
            String name = request.getName().trim();
            String code = trimToNull(request.getCode());
            if (name.length() > MAX_NAME_LENGTH) {
                throw new IllegalArgumentException("Название дисциплины не может быть длиннее 200 символов");
            }
            if (code != null && code.length() > MAX_CODE_LENGTH) {
                throw new IllegalArgumentException("Код дисциплины не может быть длиннее 20 символов");
            }
            String canonicalName = name.toLowerCase(Locale.ROOT);
            unique.put(canonicalName, new SubjectCandidate(canonicalName, name, code));
        }
        return List.copyOf(unique.values());
    }

    private String trimToNull(String value) {
        if (value == null) {
            return null;
        }
        String trimmed = value.trim();
        return trimmed.isEmpty() ? null : trimmed;
    }

    private record SubjectCandidate(String canonicalName, String name, String code) {
    }
}
