package com.storycove.controller;

import com.storycove.dto.TagDto;
import com.storycove.dto.TagAliasDto;
import com.storycove.entity.Tag;
import com.storycove.entity.TagAlias;
import com.storycove.service.TagService;
import jakarta.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

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

@RestController
@RequestMapping("/api/tags")
public class TagController {
    
    private static final Logger logger = LoggerFactory.getLogger(TagController.class);
    private final TagService tagService;
    
    public TagController(TagService tagService) {
        this.tagService = tagService;
    }
    
    @GetMapping
    public ResponseEntity<Page<TagDto>> getAllTags(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size,
            @RequestParam(defaultValue = "name") String sortBy,
            @RequestParam(defaultValue = "asc") String sortDir) {
        
        Sort sort = sortDir.equalsIgnoreCase("desc") ? 
            Sort.by(sortBy).descending() : Sort.by(sortBy).ascending();
        
        Pageable pageable = PageRequest.of(page, size, sort);
        Page<Tag> tags = tagService.findAll(pageable);
        Page<TagDto> tagDtos = tags.map(this::convertToDto);
        
        return ResponseEntity.ok(tagDtos);
    }
    
    @GetMapping("/{id}")
    public ResponseEntity<TagDto> getTagById(@PathVariable UUID id) {
        Tag tag = tagService.findById(id);
        return ResponseEntity.ok(convertToDto(tag));
    }
    
    @PostMapping
    public ResponseEntity<TagDto> createTag(@Valid @RequestBody CreateTagRequest request) {
        Tag tag = new Tag();
        tag.setName(request.getName());
        tag.setColor(request.getColor());
        tag.setDescription(request.getDescription());
        
        Tag savedTag = tagService.create(tag);
        return ResponseEntity.status(HttpStatus.CREATED).body(convertToDto(savedTag));
    }
    
    @PutMapping("/{id}")
    public ResponseEntity<TagDto> updateTag(@PathVariable UUID id, 
                                           @Valid @RequestBody UpdateTagRequest request) {
        Tag existingTag = tagService.findById(id);
        if (request.getName() != null) {
            existingTag.setName(request.getName());
        }
        if (request.getColor() != null) {
            existingTag.setColor(request.getColor());
        }
        if (request.getDescription() != null) {
            existingTag.setDescription(request.getDescription());
        }
        
        Tag updatedTag = tagService.update(id, existingTag);
        return ResponseEntity.ok(convertToDto(updatedTag));
    }
    
    @DeleteMapping("/{id}")
    public ResponseEntity<?> deleteTag(@PathVariable UUID id) {
        tagService.delete(id);
        return ResponseEntity.ok(Map.of("message", "Tag deleted successfully"));
    }
    
    @GetMapping("/search")
    public ResponseEntity<Page<TagDto>> searchTags(
            @RequestParam String query,
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size) {
        
        Pageable pageable = PageRequest.of(page, size);
        Page<Tag> tags = tagService.searchByName(query, pageable);
        Page<TagDto> tagDtos = tags.map(this::convertToDto);
        
        return ResponseEntity.ok(tagDtos);
    }
    
    @GetMapping("/autocomplete")
    public ResponseEntity<List<TagDto>> autocompleteTags(
            @RequestParam String query,
            @RequestParam(defaultValue = "10") int limit) {
        
        List<Tag> tags = tagService.findByNameOrAliasStartingWith(query, limit);
        List<TagDto> tagDtos = tags.stream().map(this::convertToDto).collect(Collectors.toList());
        
        return ResponseEntity.ok(tagDtos);
    }
    
    @GetMapping("/popular")
    public ResponseEntity<List<TagDto>> getPopularTags(@RequestParam(defaultValue = "20") int limit) {
        List<Tag> tags = tagService.findMostUsed(limit);
        List<TagDto> tagDtos = tags.stream().map(this::convertToDto).collect(Collectors.toList());
        
        return ResponseEntity.ok(tagDtos);
    }
    
    @GetMapping("/unused")
    public ResponseEntity<List<TagDto>> getUnusedTags() {
        List<Tag> tags = tagService.findUnusedTags();
        List<TagDto> tagDtos = tags.stream().map(this::convertToDto).collect(Collectors.toList());
        
        return ResponseEntity.ok(tagDtos);
    }
    
    @GetMapping("/stats")
    public ResponseEntity<Map<String, Object>> getTagStats() {
        long totalTags = tagService.countAll();
        long usedTags = tagService.countUsedTags();
        long unusedTags = totalTags - usedTags;
        
        Map<String, Object> stats = Map.of(
            "totalTags", totalTags,
            "usedTags", usedTags,
            "unusedTags", unusedTags
        );
        
        return ResponseEntity.ok(stats);
    }
    
    @GetMapping("/collections")
    public ResponseEntity<List<TagDto>> getTagsUsedByCollections() {
        List<Tag> tags = tagService.findTagsUsedByCollections();
        List<TagDto> tagDtos = tags.stream()
                .map(this::convertToDtoWithCollectionCount)
                .collect(Collectors.toList());
        
        return ResponseEntity.ok(tagDtos);
    }
    
    // Tag alias endpoints
    @PostMapping("/{tagId}/aliases")
    public ResponseEntity<TagAliasDto> addAlias(@PathVariable UUID tagId, 
                                               @RequestBody Map<String, String> request) {
        String aliasName = request.get("aliasName");
        if (aliasName == null || aliasName.trim().isEmpty()) {
            return ResponseEntity.badRequest().build();
        }
        
        try {
            TagAlias alias = tagService.addAlias(tagId, aliasName.trim());
            TagAliasDto dto = new TagAliasDto();
            dto.setId(alias.getId());
            dto.setAliasName(alias.getAliasName());
            dto.setCanonicalTagId(alias.getCanonicalTag().getId());
            dto.setCanonicalTagName(alias.getCanonicalTag().getName());
            dto.setCreatedFromMerge(alias.getCreatedFromMerge());
            dto.setCreatedAt(alias.getCreatedAt());
            
            return ResponseEntity.status(HttpStatus.CREATED).body(dto);
        } catch (Exception e) {
            return ResponseEntity.badRequest().build();
        }
    }
    
    @DeleteMapping("/{tagId}/aliases/{aliasId}")
    public ResponseEntity<?> removeAlias(@PathVariable UUID tagId, @PathVariable UUID aliasId) {
        try {
            tagService.removeAlias(tagId, aliasId);
            return ResponseEntity.ok(Map.of("message", "Alias removed successfully"));
        } catch (Exception e) {
            return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
        }
    }
    
    @GetMapping("/resolve/{name}")
    public ResponseEntity<TagDto> resolveTag(@PathVariable String name) {
        try {
            Tag resolvedTag = tagService.resolveTagByName(name);
            if (resolvedTag != null) {
                return ResponseEntity.ok(convertToDto(resolvedTag));
            } else {
                return ResponseEntity.notFound().build();
            }
        } catch (Exception e) {
            return ResponseEntity.notFound().build();
        }
    }
    
    @PostMapping("/merge")
    public ResponseEntity<?> mergeTags(@Valid @RequestBody MergeTagsRequest request) {
        try {
            Tag resultTag = tagService.mergeTags(request.getSourceTagUUIDs(), request.getTargetTagUUID());
            return ResponseEntity.ok(convertToDto(resultTag));
        } catch (Exception e) {
            logger.error("Failed to merge tags", e);
            String errorMessage = e.getMessage() != null ? e.getMessage() : "Unknown error occurred";
            return ResponseEntity.badRequest().body(Map.of("error", errorMessage));
        }
    }
    
    @PostMapping("/merge/preview")
    public ResponseEntity<?> previewMerge(@Valid @RequestBody MergeTagsRequest request) {
        try {
            MergePreviewResponse preview = tagService.previewMerge(request.getSourceTagUUIDs(), request.getTargetTagUUID());
            return ResponseEntity.ok(preview);
        } catch (Exception e) {
            logger.error("Failed to preview merge", e);
            String errorMessage = e.getMessage() != null ? e.getMessage() : "Unknown error occurred";
            return ResponseEntity.badRequest().body(Map.of("error", errorMessage));
        }
    }
    
    @PostMapping("/suggest")
    public ResponseEntity<List<TagSuggestion>> suggestTags(@RequestBody TagSuggestionRequest request) {
        try {
            List<TagSuggestion> suggestions = tagService.suggestTags(
                request.getTitle(), 
                request.getContent(), 
                request.getSummary(),
                request.getLimit() != null ? request.getLimit() : 10
            );
            return ResponseEntity.ok(suggestions);
        } catch (Exception e) {
            logger.error("Failed to suggest tags", e);
            return ResponseEntity.ok(List.of()); // Return empty list on error
        }
    }
    
    private TagDto convertToDto(Tag tag) {
        TagDto dto = new TagDto();
        dto.setId(tag.getId());
        dto.setName(tag.getName());
        dto.setColor(tag.getColor());
        dto.setDescription(tag.getDescription());
        dto.setStoryCount(tag.getStories() != null ? tag.getStories().size() : 0);
        dto.setCollectionCount(tag.getCollections() != null ? tag.getCollections().size() : 0);
        dto.setAliasCount(tag.getAliases() != null ? tag.getAliases().size() : 0);
        dto.setCreatedAt(tag.getCreatedAt());
        // updatedAt field not present in Tag entity per spec
        
        // Convert aliases to DTOs for full context
        if (tag.getAliases() != null && !tag.getAliases().isEmpty()) {
            List<TagAliasDto> aliaseDtos = tag.getAliases().stream()
                .map(alias -> {
                    TagAliasDto aliasDto = new TagAliasDto();
                    aliasDto.setId(alias.getId());
                    aliasDto.setAliasName(alias.getAliasName());
                    aliasDto.setCanonicalTagId(alias.getCanonicalTag().getId());
                    aliasDto.setCanonicalTagName(alias.getCanonicalTag().getName());
                    aliasDto.setCreatedFromMerge(alias.getCreatedFromMerge());
                    aliasDto.setCreatedAt(alias.getCreatedAt());
                    return aliasDto;
                })
                .collect(Collectors.toList());
            dto.setAliases(aliaseDtos);
        }
        
        return dto;
    }
    
    private TagDto convertToDtoWithCollectionCount(Tag tag) {
        TagDto dto = new TagDto();
        dto.setId(tag.getId());
        dto.setName(tag.getName());
        dto.setCollectionCount(tag.getCollections() != null ? tag.getCollections().size() : 0);
        dto.setCreatedAt(tag.getCreatedAt());
        // Don't set storyCount for collection-focused endpoint
        
        return dto;
    }
    
    // Request DTOs
    public static class CreateTagRequest {
        private String name;
        private String color;
        private String description;
        
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        
        public String getColor() { return color; }
        public void setColor(String color) { this.color = color; }
        
        public String getDescription() { return description; }
        public void setDescription(String description) { this.description = description; }
    }
    
    public static class UpdateTagRequest {
        private String name;
        private String color;
        private String description;
        
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        
        public String getColor() { return color; }
        public void setColor(String color) { this.color = color; }
        
        public String getDescription() { return description; }
        public void setDescription(String description) { this.description = description; }
    }
    
    public static class MergeTagsRequest {
        private List<String> sourceTagIds;
        private String targetTagId;
        
        public List<String> getSourceTagIds() { return sourceTagIds; }
        public void setSourceTagIds(List<String> sourceTagIds) { this.sourceTagIds = sourceTagIds; }
        
        public String getTargetTagId() { return targetTagId; }
        public void setTargetTagId(String targetTagId) { this.targetTagId = targetTagId; }
        
        // Helper methods to convert to UUID
        public List<UUID> getSourceTagUUIDs() {
            return sourceTagIds != null ? sourceTagIds.stream().map(UUID::fromString).toList() : null;
        }
        
        public UUID getTargetTagUUID() {
            return targetTagId != null ? UUID.fromString(targetTagId) : null;
        }
    }
    
    public static class MergePreviewResponse {
        private String targetTagName;
        private int targetStoryCount;
        private int totalResultStoryCount;
        private List<String> aliasesToCreate;
        
        public String getTargetTagName() { return targetTagName; }
        public void setTargetTagName(String targetTagName) { this.targetTagName = targetTagName; }
        
        public int getTargetStoryCount() { return targetStoryCount; }
        public void setTargetStoryCount(int targetStoryCount) { this.targetStoryCount = targetStoryCount; }
        
        public int getTotalResultStoryCount() { return totalResultStoryCount; }
        public void setTotalResultStoryCount(int totalResultStoryCount) { this.totalResultStoryCount = totalResultStoryCount; }
        
        public List<String> getAliasesToCreate() { return aliasesToCreate; }
        public void setAliasesToCreate(List<String> aliasesToCreate) { this.aliasesToCreate = aliasesToCreate; }
    }
    
    public static class TagSuggestionRequest {
        private String title;
        private String content;
        private String summary;
        private Integer limit;
        
        public String getTitle() { return title; }
        public void setTitle(String title) { this.title = title; }
        
        public String getContent() { return content; }
        public void setContent(String content) { this.content = content; }
        
        public String getSummary() { return summary; }
        public void setSummary(String summary) { this.summary = summary; }
        
        public Integer getLimit() { return limit; }
        public void setLimit(Integer limit) { this.limit = limit; }
    }
    
    public static class TagSuggestion {
        private String tagName;
        private double confidence;
        private String reason;
        
        public TagSuggestion() {}
        
        public TagSuggestion(String tagName, double confidence, String reason) {
            this.tagName = tagName;
            this.confidence = confidence;
            this.reason = reason;
        }
        
        public String getTagName() { return tagName; }
        public void setTagName(String tagName) { this.tagName = tagName; }
        
        public double getConfidence() { return confidence; }
        public void setConfidence(double confidence) { this.confidence = confidence; }
        
        public String getReason() { return reason; }
        public void setReason(String reason) { this.reason = reason; }
    }
}