package de.oaa.xxx.admin;

import java.security.Principal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;

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 de.oaa.xxx.feedback.FeedbackEntity;
import de.oaa.xxx.feedback.FeedbackRepository;
import de.oaa.xxx.feedback.FeedbackStatus;
import de.oaa.xxx.support.SupportUserService;
import de.oaa.xxx.games.common.aufgaben.AufgabenGruppe;
import de.oaa.xxx.games.common.aufgaben.AufgabenGruppeDisplay;
import de.oaa.xxx.games.common.aufgaben.Toy;
import de.oaa.xxx.games.common.entity.AufgabenGruppeEntity;
import de.oaa.xxx.games.common.entity.ToyEntity;
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.games.common.repository.ToyRepository;
import de.oaa.xxx.games.chastity.ttlock.TTLockConfigEntity;
import de.oaa.xxx.games.chastity.ttlock.TTLockConfigRepository;
import de.oaa.xxx.meldung.MeldungEntity;
import de.oaa.xxx.meldung.MeldungRepository;
import de.oaa.xxx.meldung.MeldungStatus;
import de.oaa.xxx.subscription.SubscriptionType;
import de.oaa.xxx.subscription.UserSubscriptionEntity;
import de.oaa.xxx.subscription.UserSubscriptionRepository;
import de.oaa.xxx.user.UserEntity;
import de.oaa.xxx.user.UserRepository;
import de.oaa.xxx.user.UserService;

@RestController
@RequestMapping("/admin")
@Transactional
public class AdminController {

    private final AdminRepository adminRepository;
    private final UserRepository userRepository;
    private final UserService userService;
    private final MeldungRepository meldungRepository;
    private final FeedbackRepository feedbackRepository;
    private final SupportUserService supportUserService;
    private final AufgabenGruppeRepository aufgabenGruppeRepository;
    private final AufgabeRepository aufgabeRepository;
    private final StrafeRepository strafeRepository;
    private final SperreRepository sperreRepository;
    private final FinisherRepository finisherRepository;
    private final GruppenAboRepository gruppenAboRepository;
    private final ToyRepository toyRepository;
    private final TTLockConfigRepository ttLockConfigRepository;
    private final UserSubscriptionRepository userSubscriptionRepository;

    public AdminController(AdminRepository adminRepository, UserRepository userRepository,
                           UserService userService,
                           MeldungRepository meldungRepository,
                           FeedbackRepository feedbackRepository,
                           SupportUserService supportUserService,
                           AufgabenGruppeRepository aufgabenGruppeRepository,
                           AufgabeRepository aufgabeRepository,
                           StrafeRepository strafeRepository,
                           SperreRepository sperreRepository,
                           FinisherRepository finisherRepository,
                           GruppenAboRepository gruppenAboRepository,
                           ToyRepository toyRepository,
                           TTLockConfigRepository ttLockConfigRepository,
                           UserSubscriptionRepository userSubscriptionRepository) {
        this.adminRepository = adminRepository;
        this.userRepository = userRepository;
        this.userService = userService;
        this.meldungRepository = meldungRepository;
        this.feedbackRepository = feedbackRepository;
        this.supportUserService = supportUserService;
        this.aufgabenGruppeRepository = aufgabenGruppeRepository;
        this.aufgabeRepository = aufgabeRepository;
        this.strafeRepository = strafeRepository;
        this.sperreRepository = sperreRepository;
        this.finisherRepository = finisherRepository;
        this.gruppenAboRepository = gruppenAboRepository;
        this.toyRepository = toyRepository;
        this.ttLockConfigRepository = ttLockConfigRepository;
        this.userSubscriptionRepository = userSubscriptionRepository;
    }

    // ── DTOs ─────────────────────────────────────────────────────────────────

    record AdminDto(UUID adminId, UUID userId, String userName, AdminRolle rolle, LocalDateTime createdAt) {}

    record TtlockConfigDto(String clientId, String clientSecret, String baseUrl) {}

    record TtlockConfigRequest(String clientId, String clientSecret, String baseUrl) {}

    record MeldungDto(UUID meldungId, UUID melderId, String melderName,
                      de.oaa.xxx.meldung.MeldungZielTyp zielTyp, UUID zielId,
                      String grund, LocalDateTime gemeldetAt,
                      MeldungStatus status, UUID bearbeitetVon, LocalDateTime bearbeitetAt) {}

    record CreateAdminRequest(UUID userId, AdminRolle rolle) {}

    record StatusRequest(MeldungStatus status) {}

    record UserSearchDto(UUID userId, String name) {}

    record GiftSubscriptionRequest(UUID userId) {}

    record SubscriptionStatusDto(UUID userId, String userName, String subscriptionType,
                                 LocalDate subscribedAt, LocalDate validUntil) {}

    record FeedbackDto(UUID feedbackId, String name, String seite, String grund,
                       String text, LocalDateTime eingegangen, FeedbackStatus status,
                       String inArbeitVonName) {}

    record FeedbackAntwortRequest(String text) {}

    // ── Hilfsmethoden ────────────────────────────────────────────────────────

    private AdminEntity requireAdmin(Principal principal) {
        var user = userService.requireUser(principal);
        return adminRepository.findByUserId(user.getUserId())
                .orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(
                        org.springframework.http.HttpStatus.FORBIDDEN, "Kein Admin"));
    }

    private AdminEntity requireSuperAdmin(Principal principal) {
        AdminEntity admin = requireAdmin(principal);
        if (admin.getRolle() != AdminRolle.SUPERADMIN) {
            throw new org.springframework.web.server.ResponseStatusException(
                    org.springframework.http.HttpStatus.FORBIDDEN, "Kein Superadmin");
        }
        return admin;
    }

    private AdminDto toDto(AdminEntity e) {
        String name = userRepository.findById(e.getUserId()).map(UserEntity::getName).orElse("?");
        return new AdminDto(e.getAdminId(), e.getUserId(), name, e.getRolle(), e.getCreatedAt());
    }

    private MeldungDto toMeldungDto(MeldungEntity e) {
        String melderName = userRepository.findById(e.getMelderId()).map(UserEntity::getName).orElse("?");
        return new MeldungDto(e.getMeldungId(), e.getMelderId(), melderName,
                e.getZielTyp(), e.getZielId(), e.getGrund(), e.getGemeldetAt(),
                e.getStatus(), e.getBearbeitetVon(), e.getBearbeitetAt());
    }

    // ── /admin/me ────────────────────────────────────────────────────────────

    @GetMapping("/me")
    public ResponseEntity<AdminDto> me(Principal principal) {
        var user = userService.requireUser(principal);
        return adminRepository.findByUserId(user.getUserId())
                .map(a -> ResponseEntity.ok(toDto(a)))
                .orElse(ResponseEntity.status(403).build());
    }

    // ── Meldungen ────────────────────────────────────────────────────────────

    @GetMapping("/meldungen")
    public ResponseEntity<List<MeldungDto>> getMeldungen(
            @RequestParam(name = "status", required = false) MeldungStatus status,
            Principal principal) {
        requireAdmin(principal);
        List<MeldungEntity> list = status != null
                ? meldungRepository.findByStatusOrderByGemeldetAtDesc(status)
                : meldungRepository.findAllByOrderByGemeldetAtDesc();
        return ResponseEntity.ok(list.stream().map(this::toMeldungDto).toList());
    }

    @PutMapping("/meldungen/{id}")
    public ResponseEntity<Void> updateMeldung(@PathVariable("id") UUID id,
                                               @RequestBody StatusRequest body,
                                               Principal principal) {
        requireAdmin(principal);
        var user = userService.requireUser(principal);
        MeldungEntity meldung = meldungRepository.findById(id)
                .orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(
                        org.springframework.http.HttpStatus.NOT_FOUND));
        meldung.setStatus(body.status());
        meldung.setBearbeitetVon(user.getUserId());
        meldung.setBearbeitetAt(LocalDateTime.now());
        return ResponseEntity.noContent().build();
    }

    // ── Aufgabengruppen ──────────────────────────────────────────────────────

    @GetMapping("/aufgabengruppen")
    public ResponseEntity<List<AufgabenGruppe>> getAufgabengruppen(Principal principal) {
        requireAdmin(principal);
        List<AufgabenGruppeEntity> list = aufgabenGruppeRepository
                .findByUserIdIsNull(PageRequest.of(0, 1000)).getContent();
        return ResponseEntity.ok(list.stream().map(AufgabenGruppeEntity::toAufgabenGruppe).toList());
    }

    @PostMapping("/aufgabengruppen")
    public ResponseEntity<AufgabenGruppeDisplay> createAufgabengruppe(
            @RequestBody AufgabenGruppe gruppe, Principal principal) {
        requireAdmin(principal);
        gruppe.setUserId(null);
        gruppe.setPrivateGruppe(false);
        AufgabenGruppeEntity entity = AufgabenGruppeEntity.create(gruppe);
        aufgabenGruppeRepository.save(entity);
        return ResponseEntity.status(201).body(entity.toAufgabenGruppeDisplay());
    }

    @PutMapping("/aufgabengruppen/{id}")
    public ResponseEntity<Void> updateAufgabengruppe(@PathVariable("id") UUID id,
                                                      @RequestBody AufgabenGruppe gruppe,
                                                      Principal principal) {
        requireAdmin(principal);
        AufgabenGruppeEntity entity = aufgabenGruppeRepository.findById(id)
                .orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(
                        org.springframework.http.HttpStatus.NOT_FOUND));
        entity.setName(gruppe.getName());
        entity.setBeschreibung(gruppe.getBeschreibung());
        entity.setVon(gruppe.getVon());
        if (gruppe.getBild() != null) {
            entity.setBild(java.util.Base64.getDecoder().decode(gruppe.getBild()));
        }
        return ResponseEntity.noContent().build();
    }

    @DeleteMapping("/aufgabengruppen/{id}")
    public ResponseEntity<Void> deleteAufgabengruppe(@PathVariable("id") UUID id, Principal principal) {
        requireAdmin(principal);
        AufgabenGruppeEntity entity = aufgabenGruppeRepository.findById(id)
                .orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(
                        org.springframework.http.HttpStatus.NOT_FOUND));
        if (entity.getUserId() != null) {
            return ResponseEntity.status(403).build(); // Nur System-Gruppen
        }
        gruppenAboRepository.deleteByAufgabenGruppe(entity);
        aufgabeRepository.deleteAll(entity.getAufgaben());
        strafeRepository.deleteAll(entity.getStrafen());
        sperreRepository.deleteAll(entity.getSperren());
        finisherRepository.deleteAll(entity.getFinisher());
        aufgabenGruppeRepository.delete(entity);
        return ResponseEntity.noContent().build();
    }

    // ── Toys ─────────────────────────────────────────────────────────────────

    @GetMapping("/toys")
    public ResponseEntity<List<Toy>> getToys(Principal principal) {
        requireAdmin(principal);
        List<ToyEntity> list = toyRepository.findByUserIdIsNull(PageRequest.of(0, 1000, Sort.by(Sort.Direction.ASC, "name"))).getContent();
        return ResponseEntity.ok(list.stream().map(ToyEntity::toToy).toList());
    }

    @PostMapping("/toys")
    public ResponseEntity<Toy> createToy(@RequestBody Toy toy, Principal principal) {
        requireAdmin(principal);
        if (toyRepository.existsByNameIgnoreCaseAndUserIdIsNull(toy.getName())) {
            return ResponseEntity.status(409).build();
        }
        toy.setUserId(null);
        ToyEntity entity = ToyEntity.create(toy);
        toyRepository.save(entity);
        return ResponseEntity.status(201).body(entity.toToy());
    }

    @PutMapping("/toys/{id}")
    public ResponseEntity<Void> updateToy(@PathVariable("id") UUID id,
                                           @RequestBody Toy toy, Principal principal) {
        requireAdmin(principal);
        ToyEntity entity = toyRepository.findById(id)
                .orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(
                        org.springframework.http.HttpStatus.NOT_FOUND));
        if (toyRepository.existsByNameIgnoreCaseAndUserIdIsNullAndToyIdNot(toy.getName(), id)) {
            return ResponseEntity.status(409).build();
        }
        entity.setName(toy.getName());
        entity.setBeschreibung(toy.getBeschreibung());
        if (toy.getBild() != null) {
            entity.setBild(java.util.Base64.getDecoder().decode(toy.getBild()));
        }
        return ResponseEntity.noContent().build();
    }

    @DeleteMapping("/toys/{id}")
    public ResponseEntity<Void> deleteToy(@PathVariable("id") UUID id, Principal principal) {
        requireAdmin(principal);
        ToyEntity entity = toyRepository.findById(id)
                .orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(
                        org.springframework.http.HttpStatus.NOT_FOUND));
        long usage = toyRepository.countAufgabeUsage(id)
                + toyRepository.countStrafeUsage(id)
                + toyRepository.countSperreUsage(id);
        if (usage > 0) {
            return ResponseEntity.status(409).build();
        }
        toyRepository.delete(entity);
        return ResponseEntity.noContent().build();
    }

    // ── Benutzer-Suche (nur SUPERADMIN) ──────────────────────────────────────

    @GetMapping("/users/search")
    public ResponseEntity<List<UserSearchDto>> searchUsers(
            @RequestParam String q, Principal principal) {
        requireSuperAdmin(principal);
        if (q == null || q.isBlank()) return ResponseEntity.ok(List.of());
        List<UserEntity> users = userRepository.findByNameContainingIgnoreCase(q.trim());
        return ResponseEntity.ok(users.stream()
                .filter(u -> !adminRepository.existsByUserId(u.getUserId()))
                .limit(20)
                .map(u -> new UserSearchDto(u.getUserId(), u.getName()))
                .toList());
    }

    @GetMapping("/users/search/all")
    public ResponseEntity<List<UserSearchDto>> searchAllUsers(
            @RequestParam String q, Principal principal) {
        requireSuperAdmin(principal);
        if (q == null || q.isBlank()) return ResponseEntity.ok(List.of());
        List<UserEntity> users = userRepository.findByNameContainingIgnoreCase(q.trim());
        return ResponseEntity.ok(users.stream()
                .limit(20)
                .map(u -> new UserSearchDto(u.getUserId(), u.getName()))
                .toList());
    }

    // ── Admin-Verwaltung (nur SUPERADMIN) ────────────────────────────────────

    @GetMapping("/admins")
    public ResponseEntity<List<AdminDto>> getAdmins(Principal principal) {
        requireSuperAdmin(principal);
        return ResponseEntity.ok(adminRepository.findAll().stream().map(this::toDto).toList());
    }

    @PostMapping("/admins")
    public ResponseEntity<AdminDto> createAdmin(@RequestBody CreateAdminRequest request, Principal principal) {
        requireSuperAdmin(principal);
        if (!userRepository.existsById(request.userId())) {
            return ResponseEntity.status(404).build();
        }
        if (adminRepository.existsByUserId(request.userId())) {
            return ResponseEntity.status(409).build();
        }
        AdminEntity entity = AdminEntity.create(request.userId(), request.rolle());
        adminRepository.save(entity);
        return ResponseEntity.status(201).body(toDto(entity));
    }

    @DeleteMapping("/admins/{id}")
    public ResponseEntity<Void> deleteAdmin(@PathVariable("id") UUID id, Principal principal) {
        var requestingUser = userService.requireUser(principal);
        requireSuperAdmin(principal);
        AdminEntity entity = adminRepository.findById(id)
                .orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(
                        org.springframework.http.HttpStatus.NOT_FOUND));
        if (entity.getUserId().equals(requestingUser.getUserId())) {
            return ResponseEntity.status(400).build(); // Selbstlöschung verhindern
        }
        adminRepository.delete(entity);
        return ResponseEntity.noContent().build();
    }

    // ── Abonnement verschenken (nur SUPERADMIN) ──────────────────────────────

    @GetMapping("/subscriptions")
    public ResponseEntity<List<SubscriptionStatusDto>> getAllSubscriptions(Principal principal) {
        requireSuperAdmin(principal);
        var activeSubscriptions = userSubscriptionRepository
                .findByValidUntilGreaterThanEqualOrderByValidUntilDesc(LocalDate.now());
        return ResponseEntity.ok(activeSubscriptions.stream().map(sub -> {
            String name = userRepository.findById(sub.getUserId()).map(UserEntity::getName).orElse("?");
            return new SubscriptionStatusDto(sub.getUserId(), name,
                    sub.getSubscriptionType().name(), sub.getSubscribedAt(), sub.getValidUntil());
        }).toList());
    }

    @GetMapping("/subscriptions/user/{userId}")
    public ResponseEntity<SubscriptionStatusDto> getSubscriptionStatus(
            @PathVariable UUID userId, Principal principal) {
        requireSuperAdmin(principal);
        UserEntity user = userRepository.findById(userId).orElse(null);
        if (user == null) return ResponseEntity.notFound().build();
        var sub = userSubscriptionRepository
                .findTopByUserIdAndValidUntilGreaterThanEqualOrderByValidUntilDesc(userId, LocalDate.now())
                .orElse(null);
        return ResponseEntity.ok(new SubscriptionStatusDto(
                userId, user.getName(),
                sub != null ? sub.getSubscriptionType().name() : "STANDARD",
                sub != null ? sub.getSubscribedAt() : null,
                sub != null ? sub.getValidUntil() : null
        ));
    }

    @PostMapping("/subscriptions/gift")
    public ResponseEntity<SubscriptionStatusDto> giftSubscription(
            @RequestBody GiftSubscriptionRequest request, Principal principal) {
        requireSuperAdmin(principal);
        UserEntity user = userRepository.findById(request.userId()).orElse(null);
        if (user == null) return ResponseEntity.notFound().build();

        LocalDate today = LocalDate.now();
        var existing = userSubscriptionRepository
                .findTopByUserIdAndValidUntilGreaterThanEqualOrderByValidUntilDesc(request.userId(), today)
                .orElse(null);

        UserSubscriptionEntity sub = new UserSubscriptionEntity();
        sub.setUserId(request.userId());
        sub.setSubscriptionType(SubscriptionType.PREMIUM);
        sub.setSubscribedAt(today);
        // Hat der User bereits ein aktives Abo: Laufzeit um 1 Monat verlängern
        sub.setValidUntil(existing != null
                ? existing.getValidUntil().plusMonths(1)
                : today.plusMonths(1));
        sub.setCancellableFrom(null); // Geschenk, kein Vertrag
        userSubscriptionRepository.save(sub);

        return ResponseEntity.ok(new SubscriptionStatusDto(
                request.userId(), user.getName(),
                sub.getSubscriptionType().name(),
                sub.getSubscribedAt(), sub.getValidUntil()
        ));
    }

    // ── Feedback ─────────────────────────────────────────────────────────────

    private FeedbackDto toFeedbackDto(FeedbackEntity e) {
        String inArbeitName = null;
        if (e.getInArbeitVon() != null) {
            inArbeitName = userRepository.findById(e.getInArbeitVon())
                    .map(UserEntity::getName).orElse("?");
        }
        return new FeedbackDto(e.getFeedbackId(), e.getName(), e.getSeite(), e.getGrund(),
                e.getText(), e.getEingegangen(), e.getStatus(), inArbeitName);
    }

    @GetMapping("/feedback")
    public ResponseEntity<java.util.Map<String, List<FeedbackDto>>> getFeedback(Principal principal) {
        requireAdmin(principal);
        List<FeedbackDto> ungelesen = feedbackRepository
                .findByStatusOrderByEingegangenDesc(FeedbackStatus.UNGELESEN)
                .stream().map(this::toFeedbackDto).toList();
        List<FeedbackDto> inArbeit = feedbackRepository
                .findByStatusOrderByEingegangenDesc(FeedbackStatus.IN_ARBEIT)
                .stream().map(this::toFeedbackDto).toList();
        List<FeedbackDto> beantwortet = feedbackRepository
                .findByStatusOrderByEingegangenDesc(FeedbackStatus.BEANTWORTET)
                .stream().map(this::toFeedbackDto).toList();
        return ResponseEntity.ok(java.util.Map.of(
                "ungelesen",   ungelesen,
                "inArbeit",    inArbeit,
                "beantwortet", beantwortet));
    }

    @PutMapping("/feedback/{id}/annehmen")
    public ResponseEntity<Void> feedbackAnnehmen(@PathVariable("id") UUID id, Principal principal) {
        AdminEntity admin = requireAdmin(principal);
        FeedbackEntity f = feedbackRepository.findById(id)
                .orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(
                        org.springframework.http.HttpStatus.NOT_FOUND));
        if (f.getStatus() == FeedbackStatus.IN_ARBEIT) {
            // Bereits von jemand anderem in Arbeit – Konflikt
            return ResponseEntity.status(409).build();
        }
        f.setStatus(FeedbackStatus.IN_ARBEIT);
        f.setInArbeitVon(admin.getUserId());
        feedbackRepository.save(f);
        return ResponseEntity.noContent().build();
    }

    @PostMapping("/feedback/{id}/antworten")
    public ResponseEntity<Void> feedbackAntworten(@PathVariable("id") UUID id,
                                                   @RequestBody FeedbackAntwortRequest body,
                                                   Principal principal) {
        requireAdmin(principal);
        if (body.text() == null || body.text().isBlank()) {
            return ResponseEntity.badRequest().build();
        }
        FeedbackEntity f = feedbackRepository.findById(id)
                .orElseThrow(() -> new org.springframework.web.server.ResponseStatusException(
                        org.springframework.http.HttpStatus.NOT_FOUND));
        f.setStatus(FeedbackStatus.BEANTWORTET);
        feedbackRepository.save(f);

        // DM an den Nutzer senden, falls er eingeloggt war
        if (f.getUserId() != null) {
            String dm = "Ursprüngliche Nachricht\n" + f.getText() + "\n\nAntwort\n" + body.text();
            supportUserService.sendDm(f.getUserId(), dm);
        }
        return ResponseEntity.noContent().build();
    }

    // ── TTLock-Konfiguration (nur SUPERADMIN) ─────────────────────────────────

    @GetMapping("/ttlock")
    public ResponseEntity<TtlockConfigDto> getTtlockConfig(Principal principal) {
        requireSuperAdmin(principal);
        TTLockConfigEntity cfg = ttLockConfigRepository.findById(1L)
                .orElse(new TTLockConfigEntity());
        return ResponseEntity.ok(new TtlockConfigDto(
                cfg.getClientId(),
                cfg.getClientSecret(),
                cfg.getBaseUrl()
        ));
    }

    @PutMapping("/ttlock")
    public ResponseEntity<Void> saveTtlockConfig(@RequestBody TtlockConfigRequest body, Principal principal) {
        requireSuperAdmin(principal);
        TTLockConfigEntity cfg = ttLockConfigRepository.findById(1L)
                .orElseGet(TTLockConfigEntity::new);
        cfg.setClientId(body.clientId());
        cfg.setClientSecret(body.clientSecret());
        cfg.setBaseUrl(body.baseUrl());
        ttLockConfigRepository.save(cfg);
        return ResponseEntity.noContent().build();
    }
}
