package de.oaa.xxx.games.bdsm.controller;

import java.security.Principal;
import java.util.Base64;
import java.util.UUID;

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.Sort;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
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.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

import de.oaa.xxx.games.common.aufgaben.AufgabenGruppe;
import de.oaa.xxx.games.common.aufgaben.AufgabenGruppeList;
import de.oaa.xxx.games.common.aufgaben.AufgabenGruppePage;
import de.oaa.xxx.games.common.aufgaben.AufgabenGruppeService;
import de.oaa.xxx.games.common.entity.AufgabenGruppeEntity;
import de.oaa.xxx.games.common.repository.AufgabeRepository;
import de.oaa.xxx.games.common.repository.AufgabenGruppeRepository;
import de.oaa.xxx.games.common.repository.FinisherRepository;
import de.oaa.xxx.games.common.repository.GruppenAboRepository;
import de.oaa.xxx.games.common.repository.SperreRepository;
import de.oaa.xxx.games.common.repository.StrafeRepository;
import de.oaa.xxx.subscription.SubscriptionLimitService;
import de.oaa.xxx.user.UserEntity;
import de.oaa.xxx.user.UserService;

@RestController
@RequestMapping("/gruppe")
@Transactional
public class AufgabenGruppeController {

    private static final Logger LOGGER = LoggerFactory.getLogger(AufgabenGruppeController.class);
    private static final int DEFAULT_PAGE_SIZE = 5;

    private final AufgabenGruppeRepository gruppeRepository;
    private final AufgabeRepository aufgabeRepository;
    private final StrafeRepository strafeRepository;
    private final SperreRepository sperreRepository;
    private final FinisherRepository finisherRepository;
    private final GruppenAboRepository aboRepository;
    private final AufgabenGruppeService aufgabenGruppeService;
    private final SubscriptionLimitService limitService;
    private final UserService userService;

    public AufgabenGruppeController(AufgabenGruppeRepository gruppeRepository,
                                     AufgabeRepository aufgabeRepository,
                                     StrafeRepository strafeRepository,
                                     SperreRepository sperreRepository,
                                     FinisherRepository finisherRepository,
                                     GruppenAboRepository aboRepository,
                                     AufgabenGruppeService aufgabenGruppeService,
                                     SubscriptionLimitService limitService,
                                     UserService userService) {
        this.gruppeRepository = gruppeRepository;
        this.aufgabeRepository = aufgabeRepository;
        this.strafeRepository = strafeRepository;
        this.sperreRepository = sperreRepository;
        this.finisherRepository = finisherRepository;
        this.aboRepository = aboRepository;
        this.aufgabenGruppeService = aufgabenGruppeService;
        this.limitService = limitService;
        this.userService = userService;
    }

    // ── Paginierte Listen ──

    @GetMapping("/list/user")
    public ResponseEntity<AufgabenGruppePage> listUser(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "" + DEFAULT_PAGE_SIZE) int size,
            Principal principal) {
        UserEntity user = resolveUser(principal);
        if (user == null) return ResponseEntity.status(401).build();
        Page<AufgabenGruppeEntity> result = gruppeRepository.findByUserId(
                user.getUserId(), PageRequest.of(page, size, Sort.by("name")));
        return ResponseEntity.ok(toGruppePage(result, true));
    }

    @GetMapping("/list/system")
    public ResponseEntity<AufgabenGruppePage> listSystem(
            @RequestParam(defaultValue = "0") int page,
            @RequestParam(defaultValue = "" + DEFAULT_PAGE_SIZE) int size) {
        Page<AufgabenGruppeEntity> result = gruppeRepository.findByUserIdIsNull(
                PageRequest.of(page, size, Sort.by("name")));
        return ResponseEntity.ok(toGruppePage(result));
    }

    // ── Bestehende Endpunkte ──

    @GetMapping("/all")
    public ResponseEntity<AufgabenGruppeList> getAll(@RequestParam(required = false) String search, Principal principal) {
        UUID userId = userService.requireUser(principal).getUserId();
        String searchPattern = search != null ? "%" + search + "%" : null;
        AufgabenGruppeList list = new AufgabenGruppeList();
        list.setGruppen(gruppeRepository.listWithUserAndSearch(userId, searchPattern, PageRequest.of(0, 500))
                .stream().map(AufgabenGruppeEntity::toAufgabenGruppeDisplay).toList());
        return ResponseEntity.ok(list);
    }

    @GetMapping("/own")
    public ResponseEntity<AufgabenGruppeList> getOwn(@RequestParam UUID userId) {
        AufgabenGruppeList list = new AufgabenGruppeList();
        list.setGruppen(gruppeRepository.findByUserId(userId)
                .stream().map(AufgabenGruppeEntity::toAufgabenGruppeDisplay).toList());
        return ResponseEntity.ok(list);
    }

    @GetMapping("/{gruppeId}")
    public ResponseEntity<AufgabenGruppe> get(@PathVariable UUID gruppeId) {
        return gruppeRepository.findById(gruppeId)
                .map(entity -> ResponseEntity.ok(entity.toAufgabenGruppe()))
                .orElse(ResponseEntity.noContent().build());
    }

    // ── Anlegen ──

    @PostMapping
    public ResponseEntity<Void> create(@RequestBody AufgabenGruppe gruppe, Principal principal) {
        if (gruppe.getName() == null || gruppe.getName().isBlank()) {
            return ResponseEntity.badRequest().build();
        }
        UserEntity user = resolveUser(principal);
        if (user == null) return ResponseEntity.status(401).build();

        if (gruppeRepository.countByUserId(user.getUserId()) >= limitService.maxTaskGroups(user.getUserId())) {
            return ResponseEntity.status(409).build();
        }

        AufgabenGruppeEntity entity = AufgabenGruppeEntity.create(gruppe);
        entity.setUserId(user.getUserId());
        entity.setPrivateGruppe(true);
        gruppeRepository.save(entity);
        LOGGER.debug("User {} hat AufgabenGruppe '{}' ({}) erstellt", user.getUserId(), entity.getName(), entity.getGruppenId());
        return ResponseEntity.created(
                ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(entity.getGruppenId()).toUri()
        ).build();
    }

    // ── Bearbeiten ──

    @PutMapping("/{gruppeId}")
    public ResponseEntity<Void> update(@PathVariable UUID gruppeId,
                                        @RequestBody AufgabenGruppe gruppe,
                                        Principal principal) {
        if (gruppe.getName() == null || gruppe.getName().isBlank()) {
            return ResponseEntity.badRequest().build();
        }
        UserEntity user = resolveUser(principal);
        if (user == null) return ResponseEntity.status(401).build();

        AufgabenGruppeEntity entity = gruppeRepository.findById(gruppeId).orElse(null);
        if (entity == null) return ResponseEntity.notFound().build();
        if (!user.getUserId().equals(entity.getUserId())) return ResponseEntity.status(403).build();

        entity.setName(gruppe.getName().trim());
        entity.setBeschreibung(gruppe.getBeschreibung());
        entity.setVon(gruppe.getVon());
        entity.setPrivateGruppe(gruppe.isPrivateGruppe());
        if (gruppe.getBild() != null) {
            entity.setBild(Base64.getDecoder().decode(gruppe.getBild()));
        }
        gruppeRepository.save(entity);
        LOGGER.debug("User {} hat AufgabenGruppe {} aktualisiert", user.getUserId(), gruppeId);
        return ResponseEntity.ok().build();
    }

    // ── Kopieren (Systemgruppe → eigene) ──

    @PostMapping("/copy/{gruppeId}")
    public ResponseEntity<Void> copy(@PathVariable UUID gruppeId, Principal principal) {
        UserEntity user = resolveUser(principal);
        if (user == null) return ResponseEntity.status(401).build();
        try {
            aufgabenGruppeService.copyGruppe(gruppeId, user.getUserId());
            return ResponseEntity.status(201).build();
        } catch (IllegalStateException e) {
            return ResponseEntity.status(409).build();
        } catch (IllegalArgumentException e) {
            String msg = e.getMessage();
            if (msg != null && msg.contains("nicht gefunden")) return ResponseEntity.notFound().build();
            return ResponseEntity.status(403).build();
        }
    }

    // ── Löschen ──

    @DeleteMapping("/{gruppeId}")
    public ResponseEntity<Void> deleteById(@PathVariable UUID gruppeId, Principal principal) {
        UserEntity user = resolveUser(principal);
        if (user == null) return ResponseEntity.status(401).build();

        AufgabenGruppeEntity entity = gruppeRepository.findById(gruppeId).orElse(null);
        if (entity == null) return ResponseEntity.noContent().build();
        if (!user.getUserId().equals(entity.getUserId())) return ResponseEntity.status(403).build();

        try {
            aboRepository.deleteByAufgabenGruppe(entity);
            aufgabeRepository.deleteAll(entity.getAufgaben());
            strafeRepository.deleteAll(entity.getStrafen());
            sperreRepository.deleteAll(entity.getSperren());
            finisherRepository.deleteAll(entity.getFinisher());
            gruppeRepository.delete(entity);
            return ResponseEntity.accepted().build();
        } catch (Exception e) {
            LOGGER.error(e.getMessage(), e);
            return ResponseEntity.internalServerError().build();
        }
    }

    @DeleteMapping
    public ResponseEntity<Void> delete(@RequestBody AufgabenGruppe gruppe) {
        try {
            gruppeRepository.findById(gruppe.getGruppenId()).ifPresent(gruppeRepository::delete);
            return ResponseEntity.accepted().build();
        } catch (Exception exception) {
            LOGGER.error(exception.getMessage(), exception);
            return ResponseEntity.internalServerError().build();
        }
    }

    // ── Hilfsmethoden ──

    private UserEntity resolveUser(Principal principal) {
        if (principal == null) return null;
        return userService.requireUser(principal);
    }

    private AufgabenGruppePage toGruppePage(Page<AufgabenGruppeEntity> page) {
        return toGruppePage(page, false);
    }

    private AufgabenGruppePage toGruppePage(Page<AufgabenGruppeEntity> page, boolean withSubscriberCount) {
        AufgabenGruppePage result = new AufgabenGruppePage();
        result.setContent(page.getContent().stream().map(entity -> {
            AufgabenGruppe g = entity.toAufgabenGruppe();
            if (withSubscriberCount) g.setSubscriberCount(aboRepository.countByAufgabenGruppe(entity));
            return g;
        }).toList());
        result.setCurrentPage(page.getNumber());
        result.setTotalPages(page.getTotalPages());
        result.setTotalElements(page.getTotalElements());
        return result;
    }
}
