package com.example.ldp.api.controller;

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

import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import com.example.ldp.api.dto.AuditDto;
import com.example.ldp.api.dto.DanceAdminRowDto;
import com.example.ldp.api.dto.DeleteDancePreviewDto;
import com.example.ldp.api.dto.PageResponse;
import com.example.ldp.api.dto.merge.DanceMergeConfirmRequest;
import com.example.ldp.api.dto.merge.DanceMergePreviewRequest;
import com.example.ldp.api.dto.merge.DanceMergePreviewResponse;
import com.example.ldp.api.dto.merge.DanceMergeResultDto;
import com.example.ldp.api.dto.merge.DuplicateGroupDto;
import com.example.ldp.domain.Dance;
import com.example.ldp.security.AuthUtil;
import com.example.ldp.service.AdminDanceService;
import com.example.ldp.service.DanceMergeService;
import com.example.ldp.service.DanceService;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

/**
 * Admin-only endpoints for dance management. Provides richer data than the
 * public catalog.
 */
@RestController
@RequestMapping("/api/admin/dances")
@RequiredArgsConstructor
@PreAuthorize("hasRole('ADMIN')")
public class AdminDanceController {

    private static final int MAX_PAGE_SIZE = 50;
    private static final int DEFAULT_PAGE_SIZE = 25;

    private final AdminDanceService adminDanceService;
    private final DanceMergeService danceMergeService;
    private final DanceService danceService;
    private final AuthUtil authUtil;

    /**
     * List all dances for admin management with optional filters and
     * pagination. Returns enriched DTOs with primary song and choreographers.
     *
     * @param page Número de pàgina (0-indexed, default 0)
     * @param size Mida de pàgina (default 25, màxim 50)
     */
    @GetMapping
    public PageResponse<DanceAdminRowDto> listDances(
            @RequestParam(required = false) String q,
            @RequestParam(required = false) Long id,
            @RequestParam(required = false) String level,
            @RequestParam(required = false) Integer counts,
            @RequestParam(required = false) Integer walls,
            @RequestParam(required = false) Integer yearFrom,
            @RequestParam(required = false) Integer yearTo,
            @RequestParam(required = false) Boolean hasPrimarySong,
            @RequestParam(required = false) String listingStatus,
            @RequestParam(required = false) Boolean hasSpec,
            @RequestParam(required = false) Boolean hasPrimaryVideo,
            @RequestParam(required = false) Boolean unknownYear,
            @RequestParam(required = false) Boolean unknownCounts,
            @RequestParam(required = false) Boolean hasChoreographer,
            @RequestParam(required = false) String choreographerCountry,
            @RequestParam(required = false, defaultValue = "name") String sortBy,
            @RequestParam(required = false, defaultValue = "asc") String sortDir,
            @RequestParam(name = "page", defaultValue = "0") int page,
            @RequestParam(name = "size", defaultValue = "25") int size
    ) {
        // Aplicar límits de seguretat
        int safePage = Math.max(page, 0);
        int safeSize = Math.min(Math.max(size, 1), MAX_PAGE_SIZE);

        return adminDanceService.listDances(q, id, level, counts, walls, yearFrom, yearTo,
                hasPrimarySong, listingStatus, hasSpec, hasPrimaryVideo, unknownYear, unknownCounts,
                hasChoreographer, choreographerCountry, sortBy, sortDir,
                safePage, safeSize);
    }

    /**
     * Canvia l'estat de publicació d'un ball. Body: { "listingStatus":
     * "PUBLISHED" }
     */
    @PatchMapping("/{id}/listing-status")
    public Map<String, String> updateListingStatus(
            @PathVariable Long id,
            @RequestBody Map<String, String> body
    ) {
        String newStatus = body.get("listingStatus");
        adminDanceService.updateListingStatus(id, newStatus);
        return Map.of("id", id.toString(), "listingStatus", newStatus);
    }

    /**
     * Audit metadata for a dance (admin-only).
     * Public detail endpoint omits these fields.
     */
    @GetMapping("/{id}/audit")
    public AuditDto getAudit(@PathVariable Long id) {
        Dance d = danceService.get(id);
        return new AuditDto(
                d.getCreatedAt(),
                d.getUpdatedAt(),
                d.getCreatedBy(),
                d.getLastModifiedBy()
        );
    }

    /**
     * Pre-flight summary before hard-deleting a dance. Reports blockers
     * (events using it, merge survivor) and the cascade footprint so the
     * admin can decide whether to delete or archive.
     */
    @GetMapping("/{id}/delete-preview")
    public DeleteDancePreviewDto previewDelete(@PathVariable Long id) {
        return adminDanceService.previewDelete(id);
    }

    /**
     * Hard-delete a dance. Admin must pass the exact dance name as
     * {@code confirmationName} query param. Refuses if any blocker is
     * present (use the archive flow instead, via PATCH /listing-status).
     */
    @DeleteMapping("/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void deleteDance(
            @PathVariable Long id,
            @RequestParam String confirmationName) {
        adminDanceService.deleteDance(id, confirmationName, authUtil.getCurrentUser());
    }

    /**
     * Associa una cançó existent a un ball com a PRIMARY.
     * Body: { "songId": 123 }
     */
    @PostMapping("/{id}/songs")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void linkSong(
            @PathVariable Long id,
            @RequestBody Map<String, Long> body
    ) {
        Long songId = body.get("songId");
        if (songId == null) {
            throw new com.example.ldp.service.exception.BadRequestException("songId is required");
        }
        adminDanceService.linkSongToDance(id, songId);
    }

    // ── Dance Merge / Deduplication ──

    @GetMapping("/duplicates")
    public List<DuplicateGroupDto> detectDuplicates() {
        return danceMergeService.detectDuplicates();
    }

    @PostMapping("/merge/preview")
    public DanceMergePreviewResponse mergePreview(
            @Valid @RequestBody DanceMergePreviewRequest request) {
        return danceMergeService.preview(request);
    }

    @PostMapping("/merge/execute")
    public DanceMergeResultDto mergeExecute(
            @Valid @RequestBody DanceMergeConfirmRequest request) {
        return danceMergeService.executeMerge(request);
    }
}
