package at.procon.ted.controller;

import at.procon.dip.runtime.condition.ConditionalOnRuntimeMode;
import at.procon.dip.runtime.config.RuntimeMode;
import at.procon.ted.model.dto.DocumentDtos.*;
import at.procon.ted.model.entity.ContractNature;
import at.procon.ted.model.entity.NoticeType;
import at.procon.ted.model.entity.ProcedureType;
import at.procon.ted.service.SearchService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.UUID;

/**
 * REST API controller for searching and retrieving TED procurement documents.
 * 
 * Provides endpoints for:
 * - Structured search with filters (country, type, dates, etc.)
 * - Semantic search using natural language queries
 * - Document retrieval by ID or publication ID
 * - Statistics and metadata
 * 
 * @author Martin.Schweitzer@procon.co.at and claude.ai
 */
@RestController
@RequestMapping("/v1/documents")
@RequiredArgsConstructor
@Slf4j
@ConditionalOnRuntimeMode(RuntimeMode.LEGACY)
@Tag(name = "Documents", description = "TED Procurement Document Search API")
public class DocumentController {

    private final SearchService searchService;

    /**
     * Search documents with structured and/or semantic filters.
     */
    @GetMapping("/search")
    @Operation(
            summary = "Search procurement documents",
            description = "Search documents using structured filters (country, type, dates) and/or semantic search with natural language queries"
    )
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "Search results returned successfully",
                    content = @Content(schema = @Schema(implementation = SearchResponse.class))),
            @ApiResponse(responseCode = "400", description = "Invalid search parameters")
    })
    public ResponseEntity<SearchResponse> searchDocuments(
            @Parameter(description = "Country code (ISO 3166-1 alpha-3, e.g., POL, DEU, FRA)")
            @RequestParam(required = false) String countryCode,

            @Parameter(description = "Multiple country codes")
            @RequestParam(required = false) List<String> countryCodes,

            @Parameter(description = "Notice type filter")
            @RequestParam(required = false) NoticeType noticeType,

            @Parameter(description = "Contract nature filter")
            @RequestParam(required = false) ContractNature contractNature,

            @Parameter(description = "Procedure type filter")
            @RequestParam(required = false) ProcedureType procedureType,

            @Parameter(description = "CPV code prefix (e.g., '33' for medical supplies)")
            @RequestParam(required = false) String cpvPrefix,

            @Parameter(description = "NUTS region code")
            @RequestParam(required = false) String nutsCode,

            @Parameter(description = "Publication date from (inclusive)")
            @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate publicationDateFrom,

            @Parameter(description = "Publication date to (inclusive)")
            @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate publicationDateTo,

            @Parameter(description = "Only documents with submission deadline after this date")
            @RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) OffsetDateTime submissionDeadlineAfter,

            @Parameter(description = "Filter by EU funding status")
            @RequestParam(required = false) Boolean euFunded,

            @Parameter(description = "Search in buyer name (case-insensitive)")
            @RequestParam(required = false) String buyerNameContains,

            @Parameter(description = "Search in project title (case-insensitive)")
            @RequestParam(required = false) String projectTitleContains,

            @Parameter(description = "Natural language semantic search query")
            @RequestParam(required = false) String q,

            @Parameter(description = "Similarity threshold for semantic search (0.0-1.0)")
            @RequestParam(required = false, defaultValue = "0.7") Double similarityThreshold,

            @Parameter(description = "Page number (0-based)")
            @RequestParam(required = false, defaultValue = "0") Integer page,

            @Parameter(description = "Page size (max 100)")
            @RequestParam(required = false, defaultValue = "20") Integer size,

            @Parameter(description = "Sort field (publicationDate, submissionDeadline, buyerName, projectTitle)")
            @RequestParam(required = false, defaultValue = "publicationDate") String sortBy,

            @Parameter(description = "Sort direction (asc, desc)")
            @RequestParam(required = false, defaultValue = "desc") String sortDirection
    ) {
        SearchRequest request = SearchRequest.builder()
                .countryCode(countryCode)
                .countryCodes(countryCodes)
                .noticeType(noticeType)
                .contractNature(contractNature)
                .procedureType(procedureType)
                .cpvPrefix(cpvPrefix)
                .nutsCode(nutsCode)
                .publicationDateFrom(publicationDateFrom)
                .publicationDateTo(publicationDateTo)
                .submissionDeadlineAfter(submissionDeadlineAfter)
                .euFunded(euFunded)
                .buyerNameContains(buyerNameContains)
                .projectTitleContains(projectTitleContains)
                .semanticQuery(q)
                .similarityThreshold(similarityThreshold)
                .page(page)
                .size(size)
                .sortBy(sortBy)
                .sortDirection(sortDirection)
                .build();

        log.debug("Search request: {}", request);
        SearchResponse response = searchService.search(request);
        return ResponseEntity.ok(response);
    }

    /**
     * Search documents using POST with request body.
     * Useful for complex queries with many parameters.
     */
    @PostMapping("/search")
    @Operation(
            summary = "Search procurement documents (POST)",
            description = "Search documents using a JSON request body for complex queries"
    )
    public ResponseEntity<SearchResponse> searchDocumentsPost(@RequestBody SearchRequest request) {
        log.debug("Search request (POST): {}", request);
        SearchResponse response = searchService.search(request);
        return ResponseEntity.ok(response);
    }

    /**
     * Get document by internal UUID.
     */
    @GetMapping("/{id}")
    @Operation(
            summary = "Get document by ID",
            description = "Retrieve full document details by internal UUID"
    )
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "Document found",
                    content = @Content(schema = @Schema(implementation = DocumentDetail.class))),
            @ApiResponse(responseCode = "404", description = "Document not found")
    })
    public ResponseEntity<DocumentDetail> getDocument(
            @Parameter(description = "Document UUID") @PathVariable UUID id) {
        return searchService.getDocumentDetail(id)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());
    }

    /**
     * Get document by TED publication ID.
     */
    @GetMapping("/publication/{publicationId}")
    @Operation(
            summary = "Get document by publication ID",
            description = "Retrieve document by TED publication ID (e.g., '00786665-2025')"
    )
    @ApiResponses(value = {
            @ApiResponse(responseCode = "200", description = "Document found"),
            @ApiResponse(responseCode = "404", description = "Document not found")
    })
    public ResponseEntity<DocumentDetail> getDocumentByPublicationId(
            @Parameter(description = "TED Publication ID") @PathVariable String publicationId) {
        return searchService.getDocumentByPublicationId(publicationId)
                .map(ResponseEntity::ok)
                .orElse(ResponseEntity.notFound().build());
    }

    /**
     * Get documents with upcoming submission deadlines.
     */
    @GetMapping("/upcoming-deadlines")
    @Operation(
            summary = "Get documents with upcoming deadlines",
            description = "List documents with submission deadlines in the future, sorted by deadline"
    )
    public ResponseEntity<List<DocumentSummary>> getUpcomingDeadlines(
            @Parameter(description = "Maximum number of results")
            @RequestParam(required = false, defaultValue = "20") Integer limit) {
        List<DocumentSummary> documents = searchService.getUpcomingDeadlines(Math.min(limit, 100));
        return ResponseEntity.ok(documents);
    }

    /**
     * Get collection statistics.
     */
    @GetMapping("/statistics")
    @Operation(
            summary = "Get collection statistics",
            description = "Retrieve statistics about the document collection including counts by country, type, and vectorization status"
    )
    public ResponseEntity<StatisticsResponse> getStatistics() {
        StatisticsResponse stats = searchService.getStatistics();
        return ResponseEntity.ok(stats);
    }

    /**
     * Get list of all countries in the collection.
     */
    @GetMapping("/metadata/countries")
    @Operation(
            summary = "Get available countries",
            description = "List all distinct country codes present in the collection"
    )
    public ResponseEntity<List<String>> getCountries() {
        List<String> countries = searchService.getDistinctCountries();
        return ResponseEntity.ok(countries);
    }

    /**
     * Get available notice types.
     */
    @GetMapping("/metadata/notice-types")
    @Operation(
            summary = "Get available notice types",
            description = "List all notice type enum values"
    )
    public ResponseEntity<NoticeType[]> getNoticeTypes() {
        return ResponseEntity.ok(NoticeType.values());
    }

    /**
     * Get available contract natures.
     */
    @GetMapping("/metadata/contract-natures")
    @Operation(
            summary = "Get available contract natures",
            description = "List all contract nature enum values"
    )
    public ResponseEntity<ContractNature[]> getContractNatures() {
        return ResponseEntity.ok(ContractNature.values());
    }

    /**
     * Get available procedure types.
     */
    @GetMapping("/metadata/procedure-types")
    @Operation(
            summary = "Get available procedure types",
            description = "List all procedure type enum values"
    )
    public ResponseEntity<ProcedureType[]> getProcedureTypes() {
        return ResponseEntity.ok(ProcedureType.values());
    }

    /**
     * Semantic search endpoint - convenience method for natural language queries.
     */
    @GetMapping("/semantic-search")
    @Operation(
            summary = "Semantic search",
            description = "Search documents using natural language query with vector similarity"
    )
    public ResponseEntity<SearchResponse> semanticSearch(
            @Parameter(description = "Natural language search query", required = true)
            @RequestParam String query,

            @Parameter(description = "Minimum similarity score (0.0-1.0)")
            @RequestParam(required = false, defaultValue = "0.7") Double threshold,

            @Parameter(description = "Country code filter")
            @RequestParam(required = false) String countryCode,

            @Parameter(description = "Notice type filter")
            @RequestParam(required = false) NoticeType noticeType,

            @Parameter(description = "Page number")
            @RequestParam(required = false, defaultValue = "0") Integer page,

            @Parameter(description = "Page size")
            @RequestParam(required = false, defaultValue = "20") Integer size
    ) {
        SearchRequest request = SearchRequest.builder()
                .semanticQuery(query)
                .similarityThreshold(threshold)
                .countryCode(countryCode)
                .noticeType(noticeType)
                .page(page)
                .size(size)
                .build();

        SearchResponse response = searchService.search(request);
        return ResponseEntity.ok(response);
    }
}
