package com.storycove.controller;

import com.storycove.dto.*;
import com.storycove.entity.Collection;
import com.storycove.entity.CollectionStory;
import com.storycove.entity.Story;
import com.storycove.entity.Tag;
import com.storycove.service.AudioCoveService;
import com.storycove.service.CollectionService;
import com.storycove.service.EPUBExportService;
import com.storycove.service.ImageService;
import com.storycove.service.ReadingTimeService;
import jakarta.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

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

@RestController
@RequestMapping("/api/collections")
public class CollectionController {
    
    private static final Logger logger = LoggerFactory.getLogger(CollectionController.class);
    
    private final CollectionService collectionService;
    private final AudioCoveService audioCoveService;
    private final ImageService imageService;
    private final ReadingTimeService readingTimeService;
    private final EPUBExportService epubExportService;

    @Autowired
    public CollectionController(CollectionService collectionService,
                              AudioCoveService audioCoveService,
                              ImageService imageService,
                              ReadingTimeService readingTimeService,
                              EPUBExportService epubExportService) {
        this.collectionService = collectionService;
        this.audioCoveService = audioCoveService;
        this.imageService = imageService;
        this.readingTimeService = readingTimeService;
        this.epubExportService = epubExportService;
    }
    
    /**
     * GET /api/collections - Search and list collections with pagination
     * IMPORTANT: Uses Typesense for all search/filter operations
     */
    @GetMapping
    public ResponseEntity<SearchResultDto<CollectionDto>> getCollections(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int limit,
            @RequestParam(required = false) String search,
            @RequestParam(required = false) List<String> tags,
            @RequestParam(defaultValue = "false") boolean archived) {
        
        
        // MANDATORY: Use Typesense for all search/filter operations
        SearchResultDto<Collection> results = collectionService.searchCollections(search, tags, archived, page, limit);
        
        // Convert to lightweight DTOs
        SearchResultDto<CollectionDto> optimizedResults = new SearchResultDto<>();
        optimizedResults.setQuery(results.getQuery());
        optimizedResults.setPage(results.getPage());
        optimizedResults.setPerPage(results.getPerPage());
        optimizedResults.setTotalHits(results.getTotalHits());
        optimizedResults.setSearchTimeMs(results.getSearchTimeMs());
        
        if (results.getResults() != null) {
            optimizedResults.setResults(results.getResults().stream()
                .map(this::mapToCollectionDto)
                .toList());
        }
        
        return ResponseEntity.ok(optimizedResults);
    }
    
    /**
     * GET /api/collections/{id} - Get collection with lightweight details (no story content)
     */
    @GetMapping("/{id}")
    public ResponseEntity<CollectionDto> getCollectionById(@PathVariable UUID id) {
        Collection collection = collectionService.findById(id);
        CollectionDto dto = mapToCollectionDto(collection);
        return ResponseEntity.ok(dto);
    }
    
    /**
     * POST /api/collections - Create new collection
     */
    @PostMapping
    public ResponseEntity<Collection> createCollection(@Valid @RequestBody CreateCollectionRequest request) {
        logger.info("Creating new collection: {}", request.getName());
        Collection collection = collectionService.createCollection(
            request.getName(),
            request.getDescription(),
            request.getTagNames(),
            request.getStoryIds()
        );
        logger.info("Successfully created collection: {} (ID: {})", collection.getName(), collection.getId());
        return ResponseEntity.status(HttpStatus.CREATED).body(collection);
    }
    
    /**
     * POST /api/collections (multipart) - Create new collection with cover image
     */
    @PostMapping(consumes = "multipart/form-data")
    public ResponseEntity<Collection> createCollectionWithImage(
            @RequestParam String name,
            @RequestParam(required = false) String description,
            @RequestParam(required = false) List<String> tags,
            @RequestParam(required = false) List<UUID> storyIds,
            @RequestParam(required = false, name = "coverImage") MultipartFile coverImage) {
        
        logger.info("Creating new collection with image: {}", name);
        try {
            // Create collection first
            Collection collection = collectionService.createCollection(name, description, tags, storyIds);
            
            // Upload cover image if provided
            if (coverImage != null && !coverImage.isEmpty()) {
                String imagePath = imageService.uploadImage(coverImage, ImageService.ImageType.COVER);
                collection.setCoverImagePath(imagePath);
                collection = collectionService.updateCollection(
                    collection.getId(), null, null, null, null
                );
            }
            
            logger.info("Successfully created collection with image: {} (ID: {})", collection.getName(), collection.getId());
            return ResponseEntity.status(HttpStatus.CREATED).body(collection);
            
        } catch (Exception e) {
            logger.error("Failed to create collection with image", e);
            return ResponseEntity.badRequest().build();
        }
    }
    
    /**
     * PUT /api/collections/{id} - Update collection metadata
     */
    @PutMapping("/{id}")
    public ResponseEntity<Collection> updateCollection(
            @PathVariable UUID id, 
            @Valid @RequestBody UpdateCollectionRequest request) {
        
        Collection collection = collectionService.updateCollection(
            id,
            request.getName(),
            request.getDescription(),
            request.getTagNames(),
            request.getRating()
        );
        
        return ResponseEntity.ok(collection);
    }
    
    /**
     * DELETE /api/collections/{id} - Delete collection
     */
    @DeleteMapping("/{id}")
    public ResponseEntity<Map<String, String>> deleteCollection(@PathVariable UUID id) {
        logger.info("Deleting collection with ID: {}", id);
        collectionService.deleteCollection(id);
        logger.info("Successfully deleted collection with ID: {}", id);
        return ResponseEntity.ok(Map.of("message", "Collection deleted successfully"));
    }
    
    /**
     * PUT /api/collections/{id}/archive - Archive/unarchive collection
     */
    @PutMapping("/{id}/archive")
    public ResponseEntity<Collection> archiveCollection(
            @PathVariable UUID id,
            @RequestBody ArchiveRequest request) {
        
        Collection collection = collectionService.archiveCollection(id, request.getArchived());
        return ResponseEntity.ok(collection);
    }
    
    /**
     * POST /api/collections/{id}/stories - Add stories to collection
     */
    @PostMapping("/{id}/stories")
    public ResponseEntity<Map<String, Object>> addStoriesToCollection(
            @PathVariable UUID id,
            @RequestBody AddStoriesRequest request) {
        
        Map<String, Object> result = collectionService.addStoriesToCollection(
            id, 
            request.getStoryIds(), 
            request.getPosition()
        );
        
        return ResponseEntity.ok(result);
    }
    
    /**
     * DELETE /api/collections/{id}/stories/{storyId} - Remove story from collection
     */
    @DeleteMapping("/{id}/stories/{storyId}")
    public ResponseEntity<Map<String, String>> removeStoryFromCollection(
            @PathVariable UUID id,
            @PathVariable UUID storyId) {
        
        collectionService.removeStoryFromCollection(id, storyId);
        return ResponseEntity.ok(Map.of("message", "Story removed from collection"));
    }
    
    /**
     * PUT /api/collections/{id}/stories/order - Reorder stories in collection
     */
    @PutMapping("/{id}/stories/order")
    public ResponseEntity<Map<String, String>> reorderStories(
            @PathVariable UUID id,
            @RequestBody ReorderStoriesRequest request) {
        
        collectionService.reorderStories(id, request.getStoryOrders());
        return ResponseEntity.ok(Map.of("message", "Stories reordered successfully"));
    }
    
    /**
     * GET /api/collections/{id}/read/{storyId} - Get story with collection context
     */
    @GetMapping("/{id}/read/{storyId}")
    public ResponseEntity<Map<String, Object>> getStoryWithCollectionContext(
            @PathVariable UUID id,
            @PathVariable UUID storyId) {
        
        Map<String, Object> result = collectionService.getStoryWithCollectionContext(id, storyId);
        return ResponseEntity.ok(result);
    }
    
    /**
     * GET /api/collections/{id}/stats - Get collection statistics
     */
    @GetMapping("/{id}/stats")
    public ResponseEntity<Map<String, Object>> getCollectionStatistics(@PathVariable UUID id) {
        Map<String, Object> stats = collectionService.getCollectionStatistics(id);
        return ResponseEntity.ok(stats);
    }
    
    /**
     * POST /api/collections/{id}/cover - Upload cover image
     */
    @PostMapping("/{id}/cover")
    public ResponseEntity<Map<String, Object>> uploadCoverImage(
            @PathVariable UUID id,
            @RequestParam("file") MultipartFile file) {
        
        try {
            String imagePath = imageService.uploadImage(file, ImageService.ImageType.COVER);
            
            // Update collection with new cover path
            collectionService.updateCollection(id, null, null, null, null);
            Collection collection = collectionService.findByIdBasic(id);
            collection.setCoverImagePath(imagePath);
            
            return ResponseEntity.ok(Map.of(
                "message", "Cover uploaded successfully",
                "coverPath", imagePath,
                "coverUrl", "/api/files/images/" + imagePath
            ));
            
        } catch (Exception e) {
            logger.error("Failed to upload collection cover", e);
            return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
        }
    }
    
    /**
     * DELETE /api/collections/{id}/cover - Remove cover image
     */
    @DeleteMapping("/{id}/cover")
    public ResponseEntity<Map<String, String>> removeCoverImage(@PathVariable UUID id) {
        Collection collection = collectionService.findByIdBasic(id);
        collection.setCoverImagePath(null);
        collectionService.updateCollection(id, null, null, null, null);
        
        return ResponseEntity.ok(Map.of("message", "Cover removed successfully"));
    }
    
    /**
     * POST /api/collections/{id}/send-to-audiocove - Send combined collection content to AudioCove
     */
    @PostMapping("/{id}/send-to-audiocove")
    public ResponseEntity<String> sendCollectionToAudioCove(@PathVariable UUID id) {
        try {
            String response = audioCoveService.ingestCollection(id);
            return ResponseEntity.ok(response);
        } catch (IllegalStateException e) {
            if (e.getMessage().contains("not configured")) {
                return ResponseEntity.status(503).body(e.getMessage());
            }
            return ResponseEntity.badRequest().body(e.getMessage());
        } catch (org.springframework.web.client.RestClientException e) {
            logger.error("AudioCove gateway error for collection {}: {}", id, e.getMessage());
            return ResponseEntity.status(502).body("AudioCove gateway error: " + e.getMessage());
        }
    }

    /**
     * POST /api/collections/reindex-typesense - Reindex all collections in Typesense
     */
    @PostMapping("/reindex-typesense")
    public ResponseEntity<Map<String, Object>> reindexCollectionsTypesense() {
        try {
            List<Collection> allCollections = collectionService.findAllWithTags();
            // Collections are not indexed in search engine yet
            return ResponseEntity.ok(Map.of(
                "success", true,
                "message", "Collections indexing not yet implemented in Solr",
                "count", allCollections.size()
            ));
        } catch (Exception e) {
            logger.error("Failed to reindex collections", e);
            return ResponseEntity.badRequest().body(Map.of(
                "success", false,
                "error", e.getMessage()
            ));
        }
    }
    
    /**
     * GET /api/collections/{id}/epub - Export collection as EPUB
     */
    @GetMapping("/{id}/epub")
    public ResponseEntity<org.springframework.core.io.Resource> exportCollectionAsEPUB(@PathVariable UUID id) {
        logger.info("Exporting collection {} to EPUB", id);
        
        try {
            Collection collection = collectionService.findById(id);
            List<Story> stories = collection.getCollectionStories().stream()
                    .sorted((cs1, cs2) -> Integer.compare(cs1.getPosition(), cs2.getPosition()))
                    .map(cs -> cs.getStory())
                    .collect(java.util.stream.Collectors.toList());
            
            if (stories.isEmpty()) {
                logger.warn("Collection {} contains no stories for export", id);
                return ResponseEntity.badRequest()
                    .body(null);
            }
            
            EPUBExportRequest request = new EPUBExportRequest();
            request.setIncludeCoverImage(true);
            request.setIncludeMetadata(true);
            request.setIncludeReadingPosition(false); // Collections don't have reading positions
            
            org.springframework.core.io.Resource resource = epubExportService.exportCollectionAsEPUB(id, request);
            String filename = epubExportService.getCollectionEPUBFilename(collection);
            
            logger.info("Successfully exported collection EPUB: {}", filename);
            
            return ResponseEntity.ok()
                .header("Content-Disposition", "attachment; filename=\"" + filename + "\"")
                .header("Content-Type", "application/epub+zip")
                .body(resource);
                
        } catch (Exception e) {
            logger.error("Error exporting collection EPUB: {}", e.getMessage(), e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
    
    /**
     * POST /api/collections/{id}/epub - Export collection as EPUB with custom options
     */
    @PostMapping("/{id}/epub")
    public ResponseEntity<org.springframework.core.io.Resource> exportCollectionAsEPUBWithOptions(
            @PathVariable UUID id, 
            @Valid @RequestBody EPUBExportRequest request) {
        logger.info("Exporting collection {} to EPUB with custom options", id);
        
        try {
            Collection collection = collectionService.findById(id);
            List<Story> stories = collection.getCollectionStories().stream()
                    .sorted((cs1, cs2) -> Integer.compare(cs1.getPosition(), cs2.getPosition()))
                    .map(cs -> cs.getStory())
                    .collect(java.util.stream.Collectors.toList());
            
            if (stories.isEmpty()) {
                logger.warn("Collection {} contains no stories for export", id);
                return ResponseEntity.badRequest()
                    .body(null);
            }
            
            org.springframework.core.io.Resource resource = epubExportService.exportCollectionAsEPUB(id, request);
            String filename = epubExportService.getCollectionEPUBFilename(collection);
            
            logger.info("Successfully exported collection EPUB with options: {}", filename);
            
            return ResponseEntity.ok()
                .header("Content-Disposition", "attachment; filename=\"" + filename + "\"")
                .header("Content-Type", "application/epub+zip")
                .body(resource);
                
        } catch (Exception e) {
            logger.error("Error exporting collection EPUB: {}", e.getMessage(), e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
    
    // Mapper methods
    
    private CollectionDto mapToCollectionDto(Collection collection) {
        CollectionDto dto = new CollectionDto();
        dto.setId(collection.getId());
        dto.setName(collection.getName());
        dto.setDescription(collection.getDescription());
        dto.setRating(collection.getRating());
        dto.setCoverImagePath(collection.getCoverImagePath());
        dto.setIsArchived(collection.getIsArchived());
        dto.setCreatedAt(collection.getCreatedAt());
        dto.setUpdatedAt(collection.getUpdatedAt());
        
        // Map tags
        if (collection.getTags() != null) {
            dto.setTags(collection.getTags().stream()
                .map(this::mapToTagDto)
                .toList());
        }
        
        // Map tag names for search results
        if (collection.getTagNames() != null) {
            dto.setTagNames(collection.getTagNames());
        }
        
        // Map collection stories (lightweight)
        if (collection.getCollectionStories() != null) {
            dto.setCollectionStories(collection.getCollectionStories().stream()
                .map(this::mapToCollectionStoryDto)
                .toList());
        }
        
        // Set calculated properties
        dto.setStoryCount(collection.getStoryCount());
        dto.setTotalWordCount(collection.getTotalWordCount());
        dto.setEstimatedReadingTime(readingTimeService.calculateReadingTime(collection.getTotalWordCount()));
        dto.setAverageStoryRating(collection.getAverageStoryRating());
        
        return dto;
    }
    
    private CollectionStoryDto mapToCollectionStoryDto(CollectionStory collectionStory) {
        CollectionStoryDto dto = new CollectionStoryDto();
        dto.setPosition(collectionStory.getPosition());
        dto.setAddedAt(collectionStory.getAddedAt());
        dto.setStory(mapToStorySummaryDto(collectionStory.getStory()));
        return dto;
    }
    
    private StorySummaryDto mapToStorySummaryDto(Story story) {
        StorySummaryDto dto = new StorySummaryDto();
        dto.setId(story.getId());
        dto.setTitle(story.getTitle());
        dto.setSummary(story.getSummary());
        dto.setDescription(story.getDescription());
        dto.setSourceUrl(story.getSourceUrl());
        dto.setCoverPath(story.getCoverPath());
        dto.setWordCount(story.getWordCount());
        dto.setRating(story.getRating());
        dto.setVolume(story.getVolume());
        dto.setCreatedAt(story.getCreatedAt());
        dto.setUpdatedAt(story.getUpdatedAt());
        dto.setPartOfSeries(story.isPartOfSeries());
        
        // Map author info
        if (story.getAuthor() != null) {
            dto.setAuthorId(story.getAuthor().getId());
            dto.setAuthorName(story.getAuthor().getName());
        }
        
        // Map series info
        if (story.getSeries() != null) {
            dto.setSeriesId(story.getSeries().getId());
            dto.setSeriesName(story.getSeries().getName());
        }
        
        // Map tags
        if (story.getTags() != null) {
            dto.setTags(story.getTags().stream()
                .map(this::mapToTagDto)
                .toList());
        }
        
        return dto;
    }
    
    private TagDto mapToTagDto(Tag tag) {
        TagDto dto = new TagDto();
        dto.setId(tag.getId());
        dto.setName(tag.getName());
        dto.setCreatedAt(tag.getCreatedAt());
        return dto;
    }
    
    // Request DTOs
    
    public static class CreateCollectionRequest {
        private String name;
        private String description;
        private List<String> tagNames;
        private List<UUID> storyIds;
        
        // Getters and setters
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        public String getDescription() { return description; }
        public void setDescription(String description) { this.description = description; }
        public List<String> getTagNames() { return tagNames; }
        public void setTagNames(List<String> tagNames) { this.tagNames = tagNames; }
        public List<UUID> getStoryIds() { return storyIds; }
        public void setStoryIds(List<UUID> storyIds) { this.storyIds = storyIds; }
    }
    
    public static class UpdateCollectionRequest {
        private String name;
        private String description;
        private List<String> tagNames;
        private Integer rating;
        
        // Getters and setters
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        public String getDescription() { return description; }
        public void setDescription(String description) { this.description = description; }
        public List<String> getTagNames() { return tagNames; }
        public void setTagNames(List<String> tagNames) { this.tagNames = tagNames; }
        public Integer getRating() { return rating; }
        public void setRating(Integer rating) { this.rating = rating; }
    }
    
    public static class ArchiveRequest {
        private Boolean archived;
        
        public Boolean getArchived() { return archived; }
        public void setArchived(Boolean archived) { this.archived = archived; }
    }
    
    public static class AddStoriesRequest {
        private List<UUID> storyIds;
        private Integer position;
        
        public List<UUID> getStoryIds() { return storyIds; }
        public void setStoryIds(List<UUID> storyIds) { this.storyIds = storyIds; }
        public Integer getPosition() { return position; }
        public void setPosition(Integer position) { this.position = position; }
    }
    
    public static class ReorderStoriesRequest {
        private List<Map<String, Object>> storyOrders;
        
        public List<Map<String, Object>> getStoryOrders() { return storyOrders; }
        public void setStoryOrders(List<Map<String, Object>> storyOrders) { this.storyOrders = storyOrders; }
    }
}