package com.gcsc.connection.service.service;

import com.gcsc.connection.common.exception.BusinessException;
import com.gcsc.connection.common.exception.ErrorCode;
import com.gcsc.connection.service.dto.ApiDetailResponse;
import com.gcsc.connection.service.dto.ApiParamResponse;
import com.gcsc.connection.service.dto.ApiSpecResponse;
import com.gcsc.connection.service.dto.CreateServiceApiRequest;
import com.gcsc.connection.service.dto.CreateServiceRequest;
import com.gcsc.connection.service.dto.SaveApiParamRequest;
import com.gcsc.connection.service.dto.SaveApiSpecRequest;
import com.gcsc.connection.service.dto.ServiceApiResponse;
import com.gcsc.connection.service.dto.ServiceResponse;
import com.gcsc.connection.service.dto.UpdateServiceApiRequest;
import com.gcsc.connection.service.dto.UpdateServiceRequest;
import com.gcsc.connection.service.entity.SnpService;
import com.gcsc.connection.service.entity.SnpServiceApi;
import com.gcsc.connection.service.entity.SnpServiceApiParam;
import com.gcsc.connection.service.entity.SnpServiceApiSpec;
import com.gcsc.connection.service.repository.SnpServiceApiParamRepository;
import com.gcsc.connection.service.repository.SnpServiceApiRepository;
import com.gcsc.connection.service.repository.SnpServiceApiSpecRepository;
import com.gcsc.connection.service.repository.SnpServiceRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Slf4j
@RequiredArgsConstructor
public class ServiceManagementService {

    private final SnpServiceRepository snpServiceRepository;
    private final SnpServiceApiRepository snpServiceApiRepository;
    private final SnpServiceApiSpecRepository snpServiceApiSpecRepository;
    private final SnpServiceApiParamRepository snpServiceApiParamRepository;

    /**
     * 전체 서비스 목록 조회
     */
    @Transactional(readOnly = true)
    public List<ServiceResponse> getServices() {
        return snpServiceRepository.findAll().stream()
            .map(ServiceResponse::from)
            .toList();
    }

    /**
     * 서비스 생성
     */
    @Transactional
    public ServiceResponse createService(CreateServiceRequest request) {
        if (snpServiceRepository.existsByServiceCode(request.serviceCode())) {
            throw new BusinessException(ErrorCode.SERVICE_CODE_DUPLICATE);
        }

        SnpService service = SnpService.builder()
            .serviceCode(request.serviceCode())
            .serviceName(request.serviceName())
            .serviceUrl(request.serviceUrl())
            .description(request.description())
            .healthCheckUrl(request.healthCheckUrl())
            .healthCheckInterval(request.healthCheckInterval())
            .build();

        SnpService saved = snpServiceRepository.save(service);
        log.info("서비스 생성 완료: {}", saved.getServiceCode());
        return ServiceResponse.from(saved);
    }

    /**
     * 서비스 수정
     */
    @Transactional
    public ServiceResponse updateService(Long id, UpdateServiceRequest request) {
        SnpService service = snpServiceRepository.findById(id)
            .orElseThrow(() -> new BusinessException(ErrorCode.SERVICE_NOT_FOUND));

        service.update(
            request.serviceName(), request.serviceUrl(), request.description(),
            request.healthCheckUrl(), request.healthCheckInterval(), request.isActive()
        );

        log.info("서비스 수정 완료: {}", service.getServiceCode());
        return ServiceResponse.from(service);
    }

    /**
     * 서비스 API 목록 조회
     */
    @Transactional(readOnly = true)
    public List<ServiceApiResponse> getServiceApis(Long serviceId) {
        snpServiceRepository.findById(serviceId)
            .orElseThrow(() -> new BusinessException(ErrorCode.SERVICE_NOT_FOUND));

        return snpServiceApiRepository.findByServiceServiceId(serviceId).stream()
            .map(ServiceApiResponse::from)
            .toList();
    }

    /**
     * API 상세 명세 조회
     */
    @Transactional(readOnly = true)
    public ApiDetailResponse getApiDetail(Long serviceId, Long apiId) {
        snpServiceRepository.findById(serviceId)
            .orElseThrow(() -> new BusinessException(ErrorCode.SERVICE_NOT_FOUND));

        SnpServiceApi api = snpServiceApiRepository.findById(apiId)
            .filter(a -> a.getService().getServiceId().equals(serviceId))
            .orElseThrow(() -> new BusinessException(ErrorCode.SERVICE_API_NOT_FOUND));

        SnpServiceApiSpec spec = snpServiceApiSpecRepository.findByApiApiId(apiId).orElse(null);
        List<SnpServiceApiParam> requestParams = snpServiceApiParamRepository
            .findByApiApiIdAndParamTypeOrderBySortOrder(apiId, "REQUEST");
        List<SnpServiceApiParam> responseParams = snpServiceApiParamRepository
            .findByApiApiIdAndParamTypeOrderBySortOrder(apiId, "RESPONSE");

        return new ApiDetailResponse(
            ServiceApiResponse.from(api),
            spec != null ? ApiSpecResponse.from(spec) : null,
            requestParams.stream().map(ApiParamResponse::from).toList(),
            responseParams.stream().map(ApiParamResponse::from).toList()
        );
    }

    /**
     * 서비스 단건 조회
     */
    @Transactional(readOnly = true)
    public ServiceResponse getService(Long id) {
        SnpService service = snpServiceRepository.findById(id)
            .orElseThrow(() -> new BusinessException(ErrorCode.SERVICE_NOT_FOUND));
        return ServiceResponse.from(service);
    }

    /**
     * 서비스 삭제 (soft delete)
     */
    @Transactional
    public void deleteService(Long id) {
        SnpService service = snpServiceRepository.findById(id)
            .orElseThrow(() -> new BusinessException(ErrorCode.SERVICE_NOT_FOUND));
        service.update(null, null, null, null, null, false);
        log.info("서비스 비활성화 완료: {}", service.getServiceCode());
    }

    /**
     * 서비스 API 수정
     */
    @Transactional
    public ServiceApiResponse updateServiceApi(Long serviceId, Long apiId, UpdateServiceApiRequest request) {
        snpServiceRepository.findById(serviceId)
            .orElseThrow(() -> new BusinessException(ErrorCode.SERVICE_NOT_FOUND));

        SnpServiceApi api = snpServiceApiRepository.findById(apiId)
            .filter(a -> a.getService().getServiceId().equals(serviceId))
            .orElseThrow(() -> new BusinessException(ErrorCode.SERVICE_API_NOT_FOUND));

        api.update(request.apiPath(), request.apiMethod(), request.apiName(),
            request.apiDomain(), request.apiSection(), request.description(), request.isActive());

        log.info("서비스 API 수정 완료: {} {}", api.getApiMethod(), api.getApiPath());
        return ServiceApiResponse.from(api);
    }

    /**
     * 서비스 API 삭제 (soft delete)
     */
    @Transactional
    public void deleteServiceApi(Long serviceId, Long apiId) {
        snpServiceRepository.findById(serviceId)
            .orElseThrow(() -> new BusinessException(ErrorCode.SERVICE_NOT_FOUND));

        SnpServiceApi api = snpServiceApiRepository.findById(apiId)
            .filter(a -> a.getService().getServiceId().equals(serviceId))
            .orElseThrow(() -> new BusinessException(ErrorCode.SERVICE_API_NOT_FOUND));

        api.update(null, null, null, null, null, null, false);
        log.info("서비스 API 비활성화 완료: {} {}", api.getApiMethod(), api.getApiPath());
    }

    /**
     * API 명세 저장 (upsert)
     */
    @Transactional
    public ApiSpecResponse saveApiSpec(Long serviceId, Long apiId, SaveApiSpecRequest request) {
        snpServiceRepository.findById(serviceId)
            .orElseThrow(() -> new BusinessException(ErrorCode.SERVICE_NOT_FOUND));

        SnpServiceApi api = snpServiceApiRepository.findById(apiId)
            .filter(a -> a.getService().getServiceId().equals(serviceId))
            .orElseThrow(() -> new BusinessException(ErrorCode.SERVICE_API_NOT_FOUND));

        SnpServiceApiSpec spec = snpServiceApiSpecRepository.findByApiApiId(apiId)
            .orElse(null);

        if (spec != null) {
            spec.update(request.sampleUrl(), request.sampleCode(),
                request.requestBodyExample(), request.responseBodyExample(),
                request.authRequired(), request.authType(), request.deprecated(),
                request.dataFormat(), request.referenceUrl(), request.note());
        } else {
            spec = SnpServiceApiSpec.builder()
                .api(api)
                .sampleUrl(request.sampleUrl())
                .sampleCode(request.sampleCode())
                .requestBodyExample(request.requestBodyExample())
                .responseBodyExample(request.responseBodyExample())
                .authRequired(request.authRequired())
                .authType(request.authType())
                .deprecated(request.deprecated())
                .dataFormat(request.dataFormat())
                .referenceUrl(request.referenceUrl())
                .note(request.note())
                .build();
            snpServiceApiSpecRepository.save(spec);
        }

        log.info("API 명세 저장 완료: apiId={}", apiId);
        return ApiSpecResponse.from(spec);
    }

    /**
     * API 파라미터 전체 교체
     */
    @Transactional
    public List<ApiParamResponse> saveApiParams(Long serviceId, Long apiId, List<SaveApiParamRequest> requests) {
        snpServiceRepository.findById(serviceId)
            .orElseThrow(() -> new BusinessException(ErrorCode.SERVICE_NOT_FOUND));

        SnpServiceApi api = snpServiceApiRepository.findById(apiId)
            .filter(a -> a.getService().getServiceId().equals(serviceId))
            .orElseThrow(() -> new BusinessException(ErrorCode.SERVICE_API_NOT_FOUND));

        snpServiceApiParamRepository.deleteByApiApiId(apiId);
        snpServiceApiParamRepository.flush();

        List<SnpServiceApiParam> params = requests.stream()
            .map(req -> SnpServiceApiParam.builder()
                .api(api)
                .paramType(req.paramType())
                .paramName(req.paramName())
                .paramMeaning(req.paramMeaning())
                .paramDescription(req.paramDescription())
                .required(req.required())
                .defaultValue(req.defaultValue())
                .inputType(req.inputType())
                .sortOrder(req.sortOrder())
                .build())
            .toList();

        List<SnpServiceApiParam> saved = snpServiceApiParamRepository.saveAll(params);
        log.info("API 파라미터 저장 완료: apiId={}, count={}", apiId, saved.size());
        return saved.stream().map(ApiParamResponse::from).toList();
    }

    /**
     * 서비스 API 생성
     */
    @Transactional
    public ServiceApiResponse createServiceApi(Long serviceId, CreateServiceApiRequest request) {
        SnpService service = snpServiceRepository.findById(serviceId)
            .orElseThrow(() -> new BusinessException(ErrorCode.SERVICE_NOT_FOUND));

        SnpServiceApi api = SnpServiceApi.builder()
            .service(service)
            .apiPath(request.apiPath())
            .apiMethod(request.apiMethod())
            .apiName(request.apiName())
            .apiDomain(request.apiDomain())
            .apiSection(request.apiSection())
            .description(request.description())
            .build();

        SnpServiceApi saved = snpServiceApiRepository.save(api);
        log.info("서비스 API 생성 완료: {} {}", saved.getApiMethod(), saved.getApiPath());
        return ServiceApiResponse.from(saved);
    }
}
