package com.iwhalecloud.byai.gateway.sandbox.runtime;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.iwhalecloud.byai.gateway.sandbox.client.OpenSandboxClient;
import com.iwhalecloud.byai.gateway.sandbox.client.model.CreateSandboxRequest;
import com.iwhalecloud.byai.gateway.sandbox.client.model.CreateSandboxResponse;
import com.iwhalecloud.byai.gateway.sandbox.client.model.RenewSandboxExpirationRequest;
import com.iwhalecloud.byai.gateway.sandbox.client.model.SandboxDetail;
import com.iwhalecloud.byai.gateway.sandbox.config.SandboxProperties;
import com.iwhalecloud.byai.gateway.sandbox.model.SandboxInfo;
import com.iwhalecloud.byai.gateway.sandbox.spec.SandboxServiceSpec;

public class OpenSandboxRuntimeProvider implements SandboxRuntimeProvider {

    private static final Logger log = LoggerFactory.getLogger(OpenSandboxRuntimeProvider.class);

    private final OpenSandboxClient openSandboxClient;
    private final OpenSandboxEndpointResolver endpointResolver;

    public OpenSandboxRuntimeProvider(OpenSandboxClient openSandboxClient) {
        this(openSandboxClient, new SandboxProperties());
    }

    public OpenSandboxRuntimeProvider(OpenSandboxClient openSandboxClient, SandboxProperties properties) {
        this.openSandboxClient = openSandboxClient;
        this.endpointResolver = new OpenSandboxEndpointResolver(openSandboxClient, properties);
    }

    @Override
    public String providerType() {
        return "opensandbox";
    }

    @Override
    public Optional<SandboxRuntimeInstance> findReusable(String userCode, String sandboxType) {
        log.info("OpenSandbox 查询可复用沙箱，userCode={}，sandboxType={}", userCode, sandboxType);
        List<SandboxDetail> sandboxes = openSandboxClient.listSandboxes(userCode, sandboxType);
        if (sandboxes == null || sandboxes.isEmpty()) {
            log.info("OpenSandbox 未找到可复用沙箱，userCode={}，sandboxType={}", userCode, sandboxType);
            return Optional.empty();
        }
        Optional<SandboxRuntimeInstance> reusable = sandboxes.stream()
            .filter(Objects::nonNull)
            .filter(d -> d.getId() != null && !d.getId().isBlank())
            .filter(d -> matchesSandboxMetadata(d, userCode, sandboxType))
            .filter(d -> SandboxRuntimeRequestFactory.isReusableSandboxState(d.getStatus()))
            .sorted(this::compareSandboxReusePreference)
            .map(d -> SandboxRuntimeInstance.builder()
                .sandboxId(d.getId())
                .createdAt(d.getCreatedAt())
                .expiresAt(d.getExpiresAt())
                .state(d.getStatus() != null ? d.getStatus().getState() : null)
                .reusable(Boolean.TRUE)
                .metadata(d.getMetadata())
                .build())
            .findFirst();
        log.info("OpenSandbox 可复用沙箱查询完成，userCode={}，sandboxType={}，selectedSandboxId={}",
            userCode, sandboxType, reusable.map(SandboxRuntimeInstance::getSandboxId).orElse(null));
        return reusable;
    }

    @Override
    public SandboxRuntimePage<SandboxRuntimeInstance> listSandboxesByMetadata(Map<String, String> metadata,
                                                                              int pageNo,
                                                                              int pageSize) {
        List<SandboxDetail> sandboxes = openSandboxClient.listSandboxesByMetadataStrict(metadata, pageNo, pageSize);
        List<SandboxRuntimeInstance> instances = sandboxes == null ? List.of() : sandboxes.stream()
            .filter(Objects::nonNull)
            .filter(d -> d.getId() != null && !d.getId().isBlank())
            .map(d -> SandboxRuntimeInstance.builder()
                .sandboxId(d.getId())
                .createdAt(d.getCreatedAt())
                .expiresAt(d.getExpiresAt())
                .state(d.getStatus() != null ? d.getStatus().getState() : null)
                .reusable(SandboxRuntimeRequestFactory.isReusableSandboxState(d.getStatus()))
                .metadata(d.getMetadata())
                .build())
            .toList();
        return SandboxRuntimePage.<SandboxRuntimeInstance>builder()
            .items(instances)
            .pageNo(Math.max(1, pageNo))
            .pageSize(Math.max(1, pageSize))
            .hasNext(instances.size() >= Math.max(1, pageSize))
            .build();
    }

    @Override
    public SandboxRuntimeInstance create(CreateSandboxRequest request,
                                         SandboxServiceSpec spec,
                                         String userCode,
                                         String sandboxType,
                                         String idempotencyKey) {
        log.info("OpenSandbox 创建沙箱，userCode={}，sandboxType={}，idempotencyKey={}", userCode, sandboxType, idempotencyKey);
        SandboxRuntimeRequestFactory.applyRuntimeMetadata(request, idempotencyKey);
        CreateSandboxResponse response = openSandboxClient.createSandbox(request, idempotencyKey);
        log.info("OpenSandbox 创建沙箱成功，userCode={}，sandboxType={}，sandboxId={}，expiresAt={}",
            userCode, sandboxType, response.getId(), response.getExpiresAt());
        return SandboxRuntimeInstance.builder()
            .sandboxId(response.getId())
            .createdAt(response.getCreatedAt())
            .expiresAt(response.getExpiresAt())
            .state(response.getStatus() != null ? response.getStatus().getState() : null)
            .reusable(SandboxRuntimeRequestFactory.isReusableSandboxState(response.getStatus()))
            .metadata(request.getMetadata())
            .build();
    }

    @Override
    public List<String> resolveEndpoints(SandboxRuntimeInstance instance, SandboxServiceSpec spec, CreateSandboxRequest request) {
        List<String> endpoints = endpointResolver.resolve(instance, spec);
        log.info("OpenSandbox 解析 endpoints 完成，sandboxId={}，endpoints={}", instance.getSandboxId(), endpoints);
        return endpoints;
    }

    @Override
    public void remove(String userCode, String sandboxType, SandboxInfo sandboxInfo) {
        if (sandboxInfo != null && sandboxInfo.getSandboxId() != null && !sandboxInfo.getSandboxId().isBlank()) {
            log.info("OpenSandbox 删除沙箱，userCode={}，sandboxType={}，sandboxId={}",
                userCode, sandboxType, sandboxInfo.getSandboxId());
            openSandboxClient.deleteSandbox(sandboxInfo.getSandboxId());
        }
    }

    @Override
    public void heartbeat(String userCode, String sandboxType, SandboxInfo sandboxInfo) {
        if (sandboxInfo == null
            || sandboxInfo.getSandboxId() == null
            || sandboxInfo.getSandboxId().isBlank()
            || sandboxInfo.getTimeoutSeconds() == null
            || sandboxInfo.getTimeoutSeconds() <= 0) {
            log.debug("OpenSandbox 跳过续约，参数不足，userCode={}，sandboxType={}，sandboxId={}，timeoutSeconds={}",
                userCode, sandboxType, sandboxInfo != null ? sandboxInfo.getSandboxId() : null,
                sandboxInfo != null ? sandboxInfo.getTimeoutSeconds() : null);
            return;
        }
        RenewSandboxExpirationRequest request = SandboxRuntimeRequestFactory.buildOpenSandboxRenewRequest(sandboxInfo);
        if (request != null) {
            log.info("OpenSandbox 续约沙箱，userCode={}，sandboxType={}，sandboxId={}，expiresAt={}",
                userCode, sandboxType, sandboxInfo.getSandboxId(), request.getExpiresAt());
            openSandboxClient.renewExpiration(sandboxInfo.getSandboxId(), request);
        }
    }

    @Override
    public boolean exists(String userCode, String sandboxType, SandboxInfo sandboxInfo) {
        return getSandbox(userCode, sandboxType, sandboxInfo)
            .map(SandboxRuntimeInstance::getReusable)
            .orElse(false);
    }

    @Override
    public Optional<SandboxRuntimeInstance> getSandbox(String userCode, String sandboxType, SandboxInfo sandboxInfo) {
        if (sandboxInfo == null || sandboxInfo.getSandboxId() == null || sandboxInfo.getSandboxId().isBlank()) {
            return Optional.empty();
        }
        SandboxDetail detail = openSandboxClient.getSandboxIfExists(sandboxInfo.getSandboxId());
        boolean reusable = detail != null && SandboxRuntimeRequestFactory.isReusableSandboxState(detail.getStatus());
        log.info("OpenSandbox 查询远端状态，userCode={}，sandboxType={}，sandboxId={}，exists={}，remoteState={}",
            userCode, sandboxType, sandboxInfo.getSandboxId(), reusable,
            detail != null && detail.getStatus() != null ? detail.getStatus().getState() : null);
        if (detail == null) {
            return Optional.empty();
        }
        return Optional.of(SandboxRuntimeInstance.builder()
            .sandboxId(detail.getId())
            .createdAt(detail.getCreatedAt())
            .expiresAt(detail.getExpiresAt())
            .state(detail.getStatus() != null ? detail.getStatus().getState() : null)
            .reusable(reusable)
            .metadata(detail.getMetadata())
            .build());
    }

    private static boolean matchesSandboxMetadata(SandboxDetail detail, String userCode, String sandboxType) {
        Map<String, String> metadata = detail.getMetadata();
        return metadata != null
            && Objects.equals(userCode, metadata.get("userCode"))
            && Objects.equals(sandboxType, metadata.get("serviceKey"));
    }

    private int compareSandboxReusePreference(SandboxDetail a, SandboxDetail b) {
        int rankA = SandboxRuntimeRequestFactory.stateRankForReuse(a.getStatus());
        int rankB = SandboxRuntimeRequestFactory.stateRankForReuse(b.getStatus());
        if (rankA != rankB) {
            return Integer.compare(rankB, rankA);
        }
        if (a.getCreatedAt() == null && b.getCreatedAt() == null) {
            return 0;
        }
        if (a.getCreatedAt() == null) {
            return 1;
        }
        if (b.getCreatedAt() == null) {
            return -1;
        }
        return b.getCreatedAt().compareTo(a.getCreatedAt());
    }

}
