package com.storycove.service;

import com.storycove.entity.Series;
import com.storycove.repository.SeriesRepository;
import com.storycove.service.exception.DuplicateResourceException;
import com.storycove.service.exception.ResourceNotFoundException;
import jakarta.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import java.util.List;
import java.util.Optional;
import java.util.UUID;

@Service
@Validated
@Transactional
public class SeriesService {
    
    private static final Logger logger = LoggerFactory.getLogger(SeriesService.class);

    private final SeriesRepository seriesRepository;

    @Autowired
    public SeriesService(SeriesRepository seriesRepository) {
        this.seriesRepository = seriesRepository;
    }

    @Transactional(readOnly = true)
    public List<Series> findAll() {
        return seriesRepository.findAll();
    }

    @Transactional(readOnly = true)
    public Page<Series> findAll(Pageable pageable) {
        return seriesRepository.findAll(pageable);
    }

    @Transactional(readOnly = true)
    public Series findById(UUID id) {
        return seriesRepository.findById(id)
                .orElseThrow(() -> new ResourceNotFoundException("Series", id.toString()));
    }

    @Transactional(readOnly = true)
    public Optional<Series> findByIdOptional(UUID id) {
        return seriesRepository.findById(id);
    }

    @Transactional(readOnly = true)
    public Series findByName(String name) {
        return seriesRepository.findByName(name)
                .orElseThrow(() -> new ResourceNotFoundException("Series", name));
    }

    @Transactional(readOnly = true)
    public Optional<Series> findByNameOptional(String name) {
        return seriesRepository.findByName(name);
    }

    @Transactional(readOnly = true)
    public List<Series> searchByName(String name) {
        return seriesRepository.findByNameContainingIgnoreCase(name);
    }

    @Transactional(readOnly = true)
    public Page<Series> searchByName(String name, Pageable pageable) {
        return seriesRepository.findByNameContainingIgnoreCase(name, pageable);
    }

    @Transactional(readOnly = true)
    public List<Series> findSeriesWithStories() {
        return seriesRepository.findSeriesWithStories();
    }

    @Transactional(readOnly = true)
    public Page<Series> findSeriesWithStories(Pageable pageable) {
        return seriesRepository.findSeriesWithStories(pageable);
    }

    @Transactional(readOnly = true)
    public List<Series> findLongestSeries() {
        return seriesRepository.findLongestSeries();
    }

    @Transactional(readOnly = true)
    public Page<Series> findLongestSeries(Pageable pageable) {
        return seriesRepository.findLongestSeries(pageable);
    }

    @Transactional(readOnly = true)
    public List<Series> findTopRatedSeries() {
        return seriesRepository.findTopRatedSeries();
    }

    @Transactional(readOnly = true)
    public Page<Series> findTopRatedSeries(Pageable pageable) {
        return seriesRepository.findTopRatedSeries(pageable);
    }

    @Transactional(readOnly = true)
    public List<Series> findByMinimumParts(Integer minParts) {
        return seriesRepository.findByMinimumParts(minParts);
    }

    @Transactional(readOnly = true)
    public List<Series> findEmptySeries() {
        return seriesRepository.findEmptySeries();
    }

    @Transactional(readOnly = true)
    public boolean existsByName(String name) {
        return seriesRepository.existsByName(name);
    }

    public Series create(@Valid Series series) {
        validateSeriesForCreate(series);
        return seriesRepository.save(series);
    }

    public Series update(UUID id, @Valid Series seriesUpdates) {
        Series existingSeries = findById(id);
        
        // Check for name conflicts if name is being changed
        if (!existingSeries.getName().equals(seriesUpdates.getName()) && 
            existsByName(seriesUpdates.getName())) {
            throw new DuplicateResourceException("Series", seriesUpdates.getName());
        }

        updateSeriesFields(existingSeries, seriesUpdates);
        return seriesRepository.save(existingSeries);
    }

    public void delete(UUID id) {
        Series series = findById(id);
        
        // Check if series has stories
        if (!series.getStories().isEmpty()) {
            throw new IllegalStateException("Cannot delete series with existing stories. Delete stories first or remove them from the series.");
        }
        
        seriesRepository.delete(series);
    }

    // Mark complete/incomplete methods removed - isComplete field not in spec

    public List<Series> deleteEmptySeries() {
        List<Series> emptySeries = findEmptySeries();
        seriesRepository.deleteAll(emptySeries);
        return emptySeries;
    }

    public Series findOrCreate(String name) {
        return findByNameOptional(name)
                .orElseGet(() -> create(new Series(name)));
    }

    public Series findOrCreate(String name, String description) {
        return findByNameOptional(name)
                .orElseGet(() -> create(new Series(name, description)));
    }

    @Transactional(readOnly = true)
    public long countRecentSeries(int days) {
        java.time.LocalDateTime cutoffDate = java.time.LocalDateTime.now().minusDays(days);
        return seriesRepository.countRecentSeries(cutoffDate);
    }

    @Transactional(readOnly = true)
    public long getTotalCount() {
        return seriesRepository.count();
    }

    @Transactional(readOnly = true)
    public long getActiveSeriesCount() {
        return findSeriesWithStories().size();
    }

    @Transactional(readOnly = true)
    public long countAll() {
        return seriesRepository.count();
    }
    
    @Transactional(readOnly = true)
    public long countSeriesWithStories() {
        return seriesRepository.countSeriesWithStories();
    }
    
    @Transactional(readOnly = true)
    public List<Series> findSeriesWithStoriesLimited(Pageable pageable) {
        return seriesRepository.findSeriesWithStories(pageable).getContent();
    }
    
    @Transactional(readOnly = true)
    public List<Series> findMostPopular(int limit) {
        return seriesRepository.findLongestSeries().stream()
                .limit(limit)
                .collect(java.util.stream.Collectors.toList());
    }

    private void validateSeriesForCreate(Series series) {
        if (existsByName(series.getName())) {
            throw new DuplicateResourceException("Series", series.getName());
        }
    }

    private void updateSeriesFields(Series existing, Series updates) {
        if (updates.getName() != null) {
            existing.setName(updates.getName());
        }
        if (updates.getDescription() != null) {
            existing.setDescription(updates.getDescription());
        }
        // isComplete field not in spec
    }
}