package com.tasteby.controller;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tasteby.domain.Channel;
import com.tasteby.security.AuthUtil;
import com.tasteby.service.CacheService;
import com.tasteby.service.ChannelService;
import com.tasteby.service.YouTubeService;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ResponseStatusException;

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

@RestController
@RequestMapping("/api/channels")
public class ChannelController {

    private final ChannelService channelService;
    private final YouTubeService youtubeService;
    private final CacheService cache;
    private final ObjectMapper objectMapper;

    public ChannelController(ChannelService channelService, YouTubeService youtubeService,
                             CacheService cache, ObjectMapper objectMapper) {
        this.channelService = channelService;
        this.youtubeService = youtubeService;
        this.cache = cache;
        this.objectMapper = objectMapper;
    }

    @GetMapping
    public List<Channel> list() {
        String key = cache.makeKey("channels");
        String cached = cache.getRaw(key);
        if (cached != null) {
            try {
                return objectMapper.readValue(cached, new TypeReference<List<Channel>>() {});
            } catch (Exception ignored) {}
        }
        var result = channelService.findAllActive();
        cache.set(key, result);
        return result;
    }

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public Map<String, Object> create(@RequestBody Map<String, String> body) {
        AuthUtil.requireAdmin();
        String channelId = body.get("channel_id");
        String channelName = body.get("channel_name");
        String titleFilter = body.get("title_filter");
        // #295 — body 필수값 가드 (NOT NULL 컬럼에 빈 값 들어가 500 나는 것 방지)
        if (channelId == null || channelId.isBlank()) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "channel_id는 필수입니다");
        }
        if (channelName == null || channelName.isBlank()) {
            throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "channel_name은 필수입니다");
        }
        try {
            String id = channelService.create(channelId, channelName, titleFilter);
            // #333 — 전체 flush 대신 channels 키만 evict (다른 모듈 캐시 보존)
            cache.del(cache.makeKey("channels"));
            return Map.of("id", id, "channel_id", channelId);
        } catch (DataIntegrityViolationException e) {
            // #295 — 유니크 충돌을 메시지 문자열 매칭 대신 typed 예외로 감지 (제약명 변경에도 견고).
            throw new ResponseStatusException(HttpStatus.CONFLICT, "Channel already exists");
        }
    }

    @PostMapping("/{channelId}/scan")
    public Map<String, Object> scan(@PathVariable String channelId,
                                    @RequestParam(defaultValue = "false") boolean full) {
        AuthUtil.requireAdmin();
        var result = youtubeService.scanChannel(channelId, full);
        if (result == null) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Channel not found");
        }
        cache.flush();
        return result;
    }

    @PutMapping("/{id}")
    public Map<String, Object> update(@PathVariable String id, @RequestBody Map<String, Object> body) {
        AuthUtil.requireAdmin();
        Integer sortOrder = body.get("sort_order") != null ? ((Number) body.get("sort_order")).intValue() : null;
        channelService.update(id, (String) body.get("description"), (String) body.get("tags"), sortOrder);
        cache.flush();
        return Map.of("ok", true);
    }

    @DeleteMapping("/{channelId}")
    public Map<String, Object> delete(@PathVariable String channelId) {
        AuthUtil.requireAdmin();
        if (!channelService.deactivate(channelId)) {
            throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Channel not found");
        }
        cache.flush();
        return Map.of("ok", true);
    }
}
