package com.zioinfo.kintex.tenant;

import org.springframework.stereotype.Component;

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

/**
 * 테넌트 해소 캐시 — {@code tenant} 마스터를 메모리에 적재해 요청당 DB 조회를 피한다(소규모 마스터).
 * <p>해소 우선순위(§1A-3): ① {@code X-Tenant-Id} 헤더 → ② Host 도메인 매칭 → ③ 기준 테넌트('kintex').
 * 캐시 미스(신규 온보딩 직후) 시 1회 재적재 후 재판정. 등록 API는 {@link #invalidate()}로 무효화한다.
 */
@Component
public class TenantResolver {

    private final TenantMapper mapper;

    /** id → 존재 여부(status 무관, 격리 스코프 존재 판정). */
    private volatile Map<String, Boolean> idSet = Map.of();
    /** 소문자 domain → tenant id. */
    private volatile Map<String, String> domainToId = Map.of();

    public TenantResolver(TenantMapper mapper) {
        this.mapper = mapper;
    }

    /** 알려진 테넌트인지(캐시 미스 시 1회 재적재). 대문자 정규화 매칭 — 소문자 X-Tenant-Id('kintex') 하위호환. */
    public boolean exists(String tenantId) {
        String key = TenantContext.normalize(tenantId);
        if (key == null) {
            return false;
        }
        if (idSet.containsKey(key)) {
            return true;
        }
        reload();
        return idSet.containsKey(key);
    }

    /** Host 헤더(도메인)로 테넌트 id 해소 — 없으면 null. */
    public String resolveByDomain(String host) {
        if (host == null || host.isBlank()) {
            return null;
        }
        String h = normalizeHost(host);
        String id = domainToId.get(h);
        if (id == null) {
            reload();
            id = domainToId.get(h);
        }
        return id;
    }

    /** 마스터 변경(온보딩 등록) 후 캐시 무효화. */
    public void invalidate() {
        reload();
    }

    private synchronized void reload() {
        List<Map<String, Object>> rows = mapper.findAllForResolve();
        Map<String, Boolean> ids = new HashMap<>();
        Map<String, String> domains = new HashMap<>();
        if (rows != null) {
            for (Map<String, Object> r : rows) {
                String id = str(r.get("id"));
                if (id == null) {
                    continue;
                }
                ids.put(id, Boolean.TRUE);
                String domain = str(r.get("domain"));
                if (domain != null && !domain.isBlank()) {
                    domains.put(normalizeHost(domain), id);
                }
            }
        }
        this.idSet = Map.copyOf(ids);
        this.domainToId = Map.copyOf(domains);
    }

    /** 포트 제거 + 소문자화(호스트/도메인 정규화). */
    private static String normalizeHost(String host) {
        String h = host.trim().toLowerCase();
        int colon = h.indexOf(':');
        return colon >= 0 ? h.substring(0, colon) : h;
    }

    private static String str(Object o) {
        return o == null ? null : String.valueOf(o);
    }
}
