package cloud.nstart.terravault.ordinis.restapi.web;

import cloud.nstart.terravault.ordinis.auth.ScopeContext;
import cloud.nstart.terravault.ordinis.domain.DataScope;
import cloud.nstart.terravault.ordinis.domain.record.DictionaryRecordRepository;
import cloud.nstart.terravault.ordinis.restapi.dto.CreateDictionaryRequest;
import cloud.nstart.terravault.ordinis.restapi.dto.DictionaryResponse;
import cloud.nstart.terravault.ordinis.restapi.dto.PatchSchemaRequest;
import cloud.nstart.terravault.ordinis.restapi.error.OrdinisException;
import cloud.nstart.terravault.ordinis.restapi.service.DictionaryDefinitionService;
import cloud.nstart.terravault.ordinis.restapi.service.SchemaPatchService;
import jakarta.validation.Valid;
import org.springframework.http.HttpStatus;
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.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

import java.time.OffsetDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

@RestController
@RequestMapping("/api/v1/dictionaries")
public class DictionaryDefinitionController {

  private final DictionaryDefinitionService service;
  private final ScopeContext scopeContext;
  private final DictionaryRecordRepository recordRepository;
  private final SchemaPatchService schemaPatchService;

  public DictionaryDefinitionController(
      DictionaryDefinitionService service,
      ScopeContext scopeContext,
      DictionaryRecordRepository recordRepository,
      SchemaPatchService schemaPatchService) {
    this.service = service;
    this.scopeContext = scopeContext;
    this.recordRepository = recordRepository;
    this.schemaPatchService = schemaPatchService;
  }

  @GetMapping
  public List<DictionaryResponse> list() {
    Set<DataScope> allowedScopes = scopeContext.currentScopes();
    Map<UUID, Long> counts = countsByDictionary(allowedScopes);
    return service.findAll().stream()
        .filter(d -> scopeContext.canAccess(d.getScope()))
        .map(d -> DictionaryResponse.from(d, counts.getOrDefault(d.getId(), 0L)))
        .toList();
  }

  @GetMapping("/{name}")
  public DictionaryResponse get(@PathVariable String name) {
    var d = service.findByName(name);
    if (!scopeContext.canAccess(d.getScope())) {
      throw OrdinisException.notFound(
          "dictionary_not_found", "Dictionary not found: " + name);
    }
    Map<UUID, Long> counts = countsByDictionary(scopeContext.currentScopes());
    return DictionaryResponse.from(d, counts.getOrDefault(d.getId(), 0L));
  }

  private Map<UUID, Long> countsByDictionary(Set<DataScope> allowedScopes) {
    Map<UUID, Long> counts = new HashMap<>();
    for (Object[] row :
        recordRepository.countActiveGroupedByDictionary(allowedScopes, OffsetDateTime.now())) {
      counts.put((UUID) row[0], (Long) row[1]);
    }
    return counts;
  }

  @PostMapping
  @ResponseStatus(HttpStatus.CREATED)
  public DictionaryResponse create(@Valid @RequestBody CreateDictionaryRequest req) {
    DataScope newScope = req.scope();
    if (newScope != null && !scopeContext.canAccess(newScope)) {
      throw OrdinisException.forbidden(
          "scope_insufficient",
          "Недостаточно прав scope для создания словаря с scope=" + newScope + ".");
    }
    return DictionaryResponse.from(service.create(req));
  }

  @PutMapping("/{name}")
  public DictionaryResponse update(
      @PathVariable String name,
      @Valid @RequestBody CreateDictionaryRequest req) {
    var existing = service.findByName(name);
    if (!scopeContext.canAccess(existing.getScope())) {
      throw OrdinisException.notFound(
          "dictionary_not_found", "Dictionary not found: " + name);
    }
    DataScope newScope = req.scope();
    if (newScope != null && newScope != existing.getScope()
        && !scopeContext.canAccess(newScope)) {
      throw OrdinisException.forbidden(
          "scope_insufficient",
          "Недостаточно прав scope для смены словаря на scope=" + newScope + ".");
    }
    return DictionaryResponse.from(service.updateSchema(name, req));
  }

  /**
   * Inline non-breaking field patch — backend #4 из pending-endpoints brief.
   * Bumps patch component (1.4.0 → 1.4.1). Только allow-listed keys
   * (description/title/minimum/maximum/enum).
   *
   * <p>Errors:
   * <ul>
   *   <li>404 {@code dictionary_not_found}</li>
   *   <li>404 {@code field_not_found}</li>
   *   <li>409 {@code version_mismatch}</li>
   *   <li>422 {@code breaking_change_requires_draft} / {@code validation_failed}</li>
   * </ul>
   */
  @PatchMapping("/{name}/schema")
  public SchemaPatchService.PatchSchemaResponse patchSchema(
      @PathVariable String name,
      @org.springframework.web.bind.annotation.RequestBody
      @jakarta.validation.Valid PatchSchemaRequest req) {
    return schemaPatchService.patchSchema(name, req);
  }
}
