package com.storycove.controller;

import com.storycove.dto.*;
import com.storycove.entity.Author;
import com.storycove.service.AuthorService;
import com.storycove.service.ImageService;
import com.storycove.service.SearchServiceAdapter;
import jakarta.servlet.http.HttpServletRequest;
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 org.springframework.web.multipart.MultipartFile;

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

@RestController
@RequestMapping("/api/authors")
public class AuthorController {
    
    private static final Logger logger = LoggerFactory.getLogger(AuthorController.class);
    
    private final AuthorService authorService;
    private final ImageService imageService;
    private final SearchServiceAdapter searchServiceAdapter;
    
    public AuthorController(AuthorService authorService, ImageService imageService, SearchServiceAdapter searchServiceAdapter) {
        this.authorService = authorService;
        this.imageService = imageService;
        this.searchServiceAdapter = searchServiceAdapter;
    }
    
    @GetMapping
    public ResponseEntity<Page<AuthorSummaryDto>> getAllAuthors(
            @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<Author> authors = authorService.findAll(pageable);
        Page<AuthorSummaryDto> authorDtos = authors.map(this::convertToSummaryDto);
        
        return ResponseEntity.ok(authorDtos);
    }
    
    @GetMapping("/{id}")
    public ResponseEntity<AuthorDto> getAuthorById(@PathVariable UUID id) {
        Author author = authorService.findById(id);
        return ResponseEntity.ok(convertToDto(author));
    }
    
    @PostMapping
    public ResponseEntity<AuthorDto> createAuthor(@Valid @RequestBody CreateAuthorRequest request) {
        logger.info("Creating new author: {}", request.getName());
        Author author = new Author();
        updateAuthorFromRequest(author, request);
        
        Author savedAuthor = authorService.create(author);
        logger.info("Successfully created author: {} (ID: {})", savedAuthor.getName(), savedAuthor.getId());
        return ResponseEntity.status(HttpStatus.CREATED).body(convertToDto(savedAuthor));
    }
    
    @PutMapping(value = "/{id}", consumes = "multipart/form-data")
    public ResponseEntity<AuthorDto> updateAuthorMultipart(
            @PathVariable UUID id,
            @RequestParam(required = false) String name,
            @RequestParam(required = false) String notes,
            @RequestParam(required = false) List<String> urls,
            @RequestParam(required = false, name = "authorRating") Integer rating,
            @RequestParam(required = false, name = "avatar") MultipartFile avatarFile) {
        
        logger.info("Updating author with multipart data (ID: {})", id);
        try {
            Author existingAuthor = authorService.findById(id);
            
            // Update basic fields
            if (name != null && !name.trim().isEmpty()) {
                existingAuthor.setName(name.trim());
            }
            if (notes != null) {
                existingAuthor.setNotes(notes);
            }
            if (urls != null) {
                existingAuthor.setUrls(urls);
            }
            
            // Handle rating update
            if (rating != null) {
                existingAuthor.setAuthorRating(rating);
            }
            
            // Handle avatar upload if provided
            if (avatarFile != null && !avatarFile.isEmpty()) {
                String imagePath = imageService.uploadImage(avatarFile, ImageService.ImageType.AVATAR);
                existingAuthor.setAvatarImagePath(imagePath);
            }
            
            Author updatedAuthor = authorService.update(id, existingAuthor);
            logger.info("Successfully updated author: {} via multipart", updatedAuthor.getName());
            return ResponseEntity.ok(convertToDto(updatedAuthor));
            
        } catch (Exception e) {
            return ResponseEntity.badRequest().body(null);
        }
    }
    
    @PutMapping(value = "/{id}", consumes = "application/json")
    public ResponseEntity<AuthorDto> updateAuthorJson(@PathVariable UUID id, 
                                                     @Valid @RequestBody UpdateAuthorRequest request) {
        logger.info("Updating author with JSON data: {} (ID: {})", request.getName(), id);
        
        Author existingAuthor = authorService.findById(id);
        updateAuthorFromRequest(existingAuthor, request);
        
        Author updatedAuthor = authorService.update(id, existingAuthor);
        logger.info("Successfully updated author: {} via JSON", updatedAuthor.getName());
        return ResponseEntity.ok(convertToDto(updatedAuthor));
    }
    
    @PutMapping("/{id}")
    public ResponseEntity<String> updateAuthorGeneric(@PathVariable UUID id, HttpServletRequest request) {
        
        return ResponseEntity.status(415).body("Unsupported Media Type. Expected multipart/form-data or application/json");
    }
    
    @DeleteMapping("/{id}")
    public ResponseEntity<?> deleteAuthor(@PathVariable UUID id) {
        logger.info("Deleting author with ID: {}", id);
        authorService.delete(id);
        logger.info("Successfully deleted author with ID: {}", id);
        return ResponseEntity.ok(Map.of("message", "Author deleted successfully"));
    }
    
    @PostMapping("/{id}/avatar")
    public ResponseEntity<?> uploadAvatar(@PathVariable UUID id, @RequestParam("file") MultipartFile file) {
        try {
            String imagePath = imageService.uploadImage(file, ImageService.ImageType.AVATAR);
            Author author = authorService.setAvatar(id, imagePath);
            
            return ResponseEntity.ok(Map.of(
                "message", "Avatar uploaded successfully",
                "avatarPath", author.getAvatarImagePath(),
                "avatarUrl", "/api/files/images/" + author.getAvatarImagePath()
            ));
        } catch (Exception e) {
            return ResponseEntity.badRequest().body(Map.of("error", e.getMessage()));
        }
    }
    
    @DeleteMapping("/{id}/avatar")
    public ResponseEntity<?> deleteAvatar(@PathVariable UUID id) {
        authorService.removeAvatar(id);
        return ResponseEntity.ok(Map.of("message", "Avatar removed successfully"));
    }
    
    @PostMapping("/{id}/rating")
    public ResponseEntity<AuthorDto> rateAuthor(@PathVariable UUID id, @RequestBody RatingRequest request) {
        Author author = authorService.setRating(id, request.getRating());
        AuthorDto dto = convertToDto(author);
        return ResponseEntity.ok(dto);
    }
    
    @GetMapping("/{id}/debug")
    public ResponseEntity<Map<String, Object>> debugAuthor(@PathVariable UUID id) {
        Author author = authorService.findById(id);
        Integer directDbRating = null;
        String dbError = null;
        
        try {
            directDbRating = authorService.getAuthorRatingFromDb(id);
        } catch (Exception e) {
            dbError = e.getMessage();
        }
        
        Map<String, Object> debug = Map.of(
            "authorId", author.getId(),
            "authorName", author.getName(),
            "authorRating_entity", author.getAuthorRating(),
            "authorRating_direct_db", directDbRating,
            "db_error", dbError,
            "toString", author.toString()
        );
        return ResponseEntity.ok(debug);
    }
    
    @PostMapping("/{id}/test-rating/{rating}")
    public ResponseEntity<Map<String, Object>> testSetRating(@PathVariable UUID id, @PathVariable Integer rating) {
        try {
            Author author = authorService.setRating(id, rating);
            
            return ResponseEntity.ok(Map.of(
                "success", true,
                "authorRating", author.getAuthorRating(),
                "message", "Rating set successfully"
            ));
        } catch (Exception e) {
            return ResponseEntity.ok(Map.of(
                "success", false,
                "error", e.getMessage()
            ));
        }
    }
    
    @PostMapping("/{id}/test-put-rating")
    public ResponseEntity<Map<String, Object>> testPutWithRating(@PathVariable UUID id, @RequestParam Integer rating) {
        try {
            
            Author existingAuthor = authorService.findById(id);
            existingAuthor.setAuthorRating(rating);
            Author updatedAuthor = authorService.update(id, existingAuthor);
            
            
            return ResponseEntity.ok(Map.of(
                "success", true,
                "authorRating", updatedAuthor.getAuthorRating(),
                "message", "Rating updated via PUT successfully"
            ));
        } catch (Exception e) {
            return ResponseEntity.ok(Map.of(
                "success", false,
                "error", e.getMessage()
            ));
        }
    }
    
    @GetMapping("/search")
    public ResponseEntity<Page<AuthorSummaryDto>> searchAuthors(
            @RequestParam String query,
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size) {
        
        Pageable pageable = PageRequest.of(page, size);
        Page<Author> authors = authorService.searchByName(query, pageable);
        Page<AuthorSummaryDto> authorDtos = authors.map(this::convertToSummaryDto);
        
        return ResponseEntity.ok(authorDtos);
    }
    
    @GetMapping("/search-typesense")
    public ResponseEntity<SearchResultDto<AuthorDto>> searchAuthorsTypesense(
            @RequestParam(defaultValue = "*") String q,
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "20") int size,
            @RequestParam(defaultValue = "name") String sortBy,
            @RequestParam(defaultValue = "asc") String sortOrder) {
        
        // Use SearchServiceAdapter to handle routing between search engines
        List<AuthorSearchDto> authorSearchResults = searchServiceAdapter.searchAuthors(q, size);

        // Create SearchResultDto to match expected return format
        SearchResultDto<AuthorSearchDto> searchResults = new SearchResultDto<>();
        searchResults.setResults(authorSearchResults);
        searchResults.setQuery(q);
        searchResults.setPage(page);
        searchResults.setPerPage(size);
        searchResults.setTotalHits(authorSearchResults.size());
        searchResults.setSearchTimeMs(0); // SearchServiceAdapter doesn't provide timing
        
        // Convert AuthorSearchDto results to AuthorDto
        SearchResultDto<AuthorDto> results = new SearchResultDto<>();
        results.setQuery(searchResults.getQuery());
        results.setPage(searchResults.getPage());
        results.setPerPage(searchResults.getPerPage());
        results.setTotalHits(searchResults.getTotalHits());
        results.setSearchTimeMs(searchResults.getSearchTimeMs());
        
        // Handle null results gracefully
        List<AuthorDto> authorDtos = searchResults.getResults() != null 
            ? searchResults.getResults().stream()
                .map(this::convertSearchDtoToDto)
                .collect(Collectors.toList())
            : new ArrayList<>();
        results.setResults(authorDtos);
        
        return ResponseEntity.ok(results);
    }
    
    @PostMapping("/reindex-typesense")
    public ResponseEntity<Map<String, Object>> reindexAuthorsTypesense() {
        try {
            List<Author> allAuthors = authorService.findAllWithStories();
            searchServiceAdapter.bulkIndexAuthors(allAuthors);
            return ResponseEntity.ok(Map.of(
                "success", true,
                "message", "Reindexed " + allAuthors.size() + " authors",
                "count", allAuthors.size()
            ));
        } catch (Exception e) {
            logger.error("Failed to reindex authors", e);
            return ResponseEntity.ok(Map.of(
                "success", false,
                "error", e.getMessage()
            ));
        }
    }
    
    @PostMapping("/recreate-typesense-collection")
    public ResponseEntity<Map<String, Object>> recreateAuthorsCollection() {
        try {
            // This will delete the existing collection and recreate it with correct schema
            List<Author> allAuthors = authorService.findAllWithStories();
            searchServiceAdapter.bulkIndexAuthors(allAuthors);
            return ResponseEntity.ok(Map.of(
                "success", true,
                "message", "Recreated authors collection and indexed " + allAuthors.size() + " authors",
                "count", allAuthors.size()
            ));
        } catch (Exception e) {
            logger.error("Failed to recreate authors collection", e);
            return ResponseEntity.ok(Map.of(
                "success", false,
                "error", e.getMessage()
            ));
        }
    }
    
    @GetMapping("/typesense-schema")
    public ResponseEntity<Map<String, Object>> getAuthorsTypesenseSchema() {
        try {
            Map<String, Object> schema = Map.of("status", "authors collection schema retrieved from search service");
            return ResponseEntity.ok(Map.of(
                "success", true,
                "schema", schema
            ));
        } catch (Exception e) {
            logger.error("Failed to get authors schema", e);
            return ResponseEntity.ok(Map.of(
                "success", false,
                "error", e.getMessage()
            ));
        }
    }
    
    @PostMapping("/clean-author-names")
    public ResponseEntity<Map<String, Object>> cleanAuthorNames() {
        try {
            List<Author> allAuthors = authorService.findAllWithStories();
            int cleanedCount = 0;
            
            for (Author author : allAuthors) {
                String originalName = author.getName();
                String cleanedName = originalName != null ? originalName.trim() : "";
                
                if (!cleanedName.equals(originalName)) {
                    logger.info("Cleaning author name: '{}' -> '{}'", originalName, cleanedName);
                    author.setName(cleanedName);
                    authorService.update(author.getId(), author);
                    cleanedCount++;
                }
            }
            
            // Reindex all authors after cleaning
            if (cleanedCount > 0) {
                searchServiceAdapter.bulkIndexAuthors(allAuthors);
            }
            
            return ResponseEntity.ok(Map.of(
                "success", true,
                "message", "Cleaned " + cleanedCount + " author names and reindexed",
                "cleanedCount", cleanedCount,
                "totalAuthors", allAuthors.size()
            ));
        } catch (Exception e) {
            logger.error("Failed to clean author names", e);
            return ResponseEntity.ok(Map.of(
                "success", false,
                "error", e.getMessage()
            ));
        }
    }
    
    @GetMapping("/top-rated")
    public ResponseEntity<List<AuthorSummaryDto>> getTopRatedAuthors(@RequestParam(defaultValue = "10") int limit) {
        Pageable pageable = PageRequest.of(0, limit);
        List<Author> authors = authorService.findTopRated(pageable);
        List<AuthorSummaryDto> authorDtos = authors.stream().map(this::convertToSummaryDto).collect(Collectors.toList());
        
        return ResponseEntity.ok(authorDtos);
    }
    
    @PostMapping("/{id}/urls")
    public ResponseEntity<AuthorDto> addUrl(@PathVariable UUID id, @RequestBody UrlRequest request) {
        Author author = authorService.addUrl(id, request.getUrl());
        return ResponseEntity.ok(convertToDto(author));
    }
    
    @DeleteMapping("/{id}/urls")
    public ResponseEntity<AuthorDto> removeUrl(@PathVariable UUID id, @RequestBody UrlRequest request) {
        Author author = authorService.removeUrl(id, request.getUrl());
        return ResponseEntity.ok(convertToDto(author));
    }
    
    private void updateAuthorFromRequest(Author author, Object request) {
        if (request instanceof CreateAuthorRequest createReq) {
            author.setName(createReq.getName());
            author.setNotes(createReq.getNotes());
            if (createReq.getUrls() != null) {
                author.setUrls(createReq.getUrls());
            }
        } else if (request instanceof UpdateAuthorRequest updateReq) {
            if (updateReq.getName() != null) {
                author.setName(updateReq.getName());
            }
            if (updateReq.getNotes() != null) {
                author.setNotes(updateReq.getNotes());
            }
            if (updateReq.getUrls() != null) {
                author.setUrls(updateReq.getUrls());
            }
            if (updateReq.getRating() != null) {
                author.setAuthorRating(updateReq.getRating());
            }
        }
    }
    
    private AuthorDto convertToDto(Author author) {
        AuthorDto dto = new AuthorDto();
        dto.setId(author.getId());
        dto.setName(author.getName());
        dto.setNotes(author.getNotes());
        dto.setAvatarImagePath(author.getAvatarImagePath());
        
        
        dto.setAuthorRating(author.getAuthorRating());
        dto.setUrls(author.getUrls());
        dto.setStoryCount(author.getStories() != null ? author.getStories().size() : 0);
        dto.setCreatedAt(author.getCreatedAt());
        dto.setUpdatedAt(author.getUpdatedAt());
        
        // Calculate and set average story rating
        dto.setAverageStoryRating(authorService.calculateAverageStoryRating(author.getId()));
        
        
        return dto;
    }
    
    private AuthorSummaryDto convertToSummaryDto(Author author) {
        AuthorSummaryDto dto = new AuthorSummaryDto();
        dto.setId(author.getId());
        dto.setName(author.getName());
        dto.setNotes(author.getNotes());
        dto.setAvatarImagePath(author.getAvatarImagePath());
        dto.setAuthorRating(author.getAuthorRating());
        dto.setUrls(author.getUrls());
        dto.setStoryCount(author.getStories() != null ? author.getStories().size() : 0);
        dto.setCreatedAt(author.getCreatedAt());
        dto.setUpdatedAt(author.getUpdatedAt());
        
        // Calculate and set average story rating without loading all stories
        dto.setAverageStoryRating(authorService.calculateAverageStoryRating(author.getId()));
        
        return dto;
    }
    
    private AuthorDto convertSearchDtoToDto(AuthorSearchDto searchDto) {
        AuthorDto dto = new AuthorDto();
        dto.setId(searchDto.getId());
        dto.setName(searchDto.getName());
        dto.setNotes(searchDto.getNotes());
        dto.setAuthorRating(searchDto.getAuthorRating());
        dto.setAverageStoryRating(searchDto.getAverageStoryRating());
        dto.setStoryCount(searchDto.getStoryCount());
        dto.setUrls(searchDto.getUrls());
        dto.setAvatarImagePath(searchDto.getAvatarImagePath());
        dto.setCreatedAt(searchDto.getCreatedAt());
        dto.setUpdatedAt(searchDto.getUpdatedAt());
        
        return dto;
    }
    
    // Request DTOs
    public static class CreateAuthorRequest {
        private String name;
        private String notes;
        private List<String> urls;
        
        // Getters and setters
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        public String getNotes() { return notes; }
        public void setNotes(String notes) { this.notes = notes; }
        public List<String> getUrls() { return urls; }
        public void setUrls(List<String> urls) { this.urls = urls; }
    }
    
    public static class UpdateAuthorRequest {
        private String name;
        private String notes;
        private List<String> urls;
        private Integer rating;
        
        // Getters and setters
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        public String getNotes() { return notes; }
        public void setNotes(String notes) { this.notes = notes; }
        public List<String> getUrls() { return urls; }
        public void setUrls(List<String> urls) { this.urls = urls; }
        public Integer getRating() { return rating; }
        public void setRating(Integer rating) { this.rating = rating; }
    }
    
    public static class RatingRequest {
        private Integer rating;
        
        public Integer getRating() { return rating; }
        public void setRating(Integer rating) { this.rating = rating; }
    }
    
    public static class UrlRequest {
        private String url;
        
        public String getUrl() { return url; }
        public void setUrl(String url) { this.url = url; }
    }
}