package com.zioinfo.kintex.visitor.contact;

import com.zioinfo.kintex.common.error.ApiException;
import com.zioinfo.kintex.common.error.ErrorCode;
import com.zioinfo.kintex.common.text.Masking;
import com.zioinfo.kintex.visitor.contact.ContactDtos.ContactDto;
import com.zioinfo.kintex.visitor.contact.ContactDtos.MyCardDto;
import com.zioinfo.kintex.visitor.contact.ContactDtos.SaveCardRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.security.SecureRandom;
import java.util.HexFormat;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * F-C4 관람객 QR 명함 교환·컨택트 지갑 서비스.
 *
 * <p>흐름: ①내 명함 등록/조회(QR 공유 토큰) → ②상대 QR 스캔으로 교환(상호 지갑 저장) → ③지갑 목록/삭제.
 * 리드캡처(업체→관람객)와 별개인 관람객 상호 교환.
 */
@Service
public class ContactService {

    private static final String QR_PREFIX = "CONTACT|";

    private final ContactMapper mapper;
    private final SecureRandom random = new SecureRandom();

    public ContactService(ContactMapper mapper) {
        this.mapper = mapper;
    }

    /** 내 명함 조회 — 없으면 표시이름으로 최소 카드 자동 생성(공유 토큰 발급). */
    @Transactional
    public MyCardDto myCard(String userId, String fallbackDisplayName) {
        Map<String, Object> card = mapper.findMyCard(userId);
        if (card == null) {
            String token = newShareToken();
            Map<String, Object> row = new java.util.HashMap<>();
            row.put("userId", userId);
            row.put("displayName", fallbackDisplayName == null || fallbackDisplayName.isBlank()
                    ? "관람객" : fallbackDisplayName);
            row.put("company", null);
            row.put("title", null);
            row.put("emailMasked", null);
            row.put("phoneMasked", null);
            row.put("shareToken", token);
            mapper.upsertMyCard(row);
            card = mapper.findMyCard(userId);
        }
        return toCard(card);
    }

    /** 내 명함 저장/수정 — 연락처·이메일 마스킹 후 저장(원문 미보관). */
    @Transactional
    public MyCardDto saveCard(String userId, SaveCardRequest req) {
        Map<String, Object> existing = mapper.findMyCard(userId);
        String token = existing != null ? str(existing.get("shareToken")) : newShareToken();
        Map<String, Object> row = new java.util.HashMap<>();
        row.put("userId", userId);
        row.put("displayName", req.displayName().trim());
        row.put("company", blankToNull(req.company()));
        row.put("title", blankToNull(req.title()));
        row.put("emailMasked", req.email() == null || req.email().isBlank() ? null : Masking.maskEmail(req.email()));
        row.put("phoneMasked", req.phone() == null || req.phone().isBlank() ? null : Masking.maskPhone(req.phone()));
        row.put("shareToken", token);
        mapper.upsertMyCard(row);
        return toCard(mapper.findMyCard(userId));
    }

    /** QR 교환 — 상대 명함을 내 지갑에 저장하고, 내 명함도 상대 지갑에 저장(상호). */
    @Transactional
    public ContactDto exchange(String userId, String myDisplayName, String qrPayload) {
        String token = parseToken(qrPayload);
        Map<String, Object> other = mapper.findCardByShareToken(token);
        if (other == null) {
            throw new ApiException(ErrorCode.NOT_FOUND, "명함을 찾을 수 없습니다. QR을 다시 확인해 주세요.");
        }
        String otherUserId = str(other.get("userId"));
        if (userId.equals(otherUserId)) {
            throw new ApiException(ErrorCode.VALIDATION, "본인 명함은 교환할 수 없습니다.");
        }

        // 내 지갑에 상대 저장.
        String contactId = "vc-" + UUID.randomUUID();
        Map<String, Object> mineRow = contactRow(contactId, userId, otherUserId, other);
        mapper.insertContact(mineRow);

        // 상대 지갑에 나(내 명함) 저장(상호). 내 명함이 없으면 자동 생성.
        MyCardDto myCard = myCard(userId, myDisplayName);
        Map<String, Object> myAsContact = new java.util.HashMap<>();
        myAsContact.put("id", "vc-" + UUID.randomUUID());
        myAsContact.put("ownerUserId", otherUserId);
        myAsContact.put("contactUserId", userId);
        myAsContact.put("displayName", myCard.displayName());
        myAsContact.put("company", myCard.company());
        myAsContact.put("title", myCard.title());
        myAsContact.put("emailMasked", myCard.emailMasked());
        myAsContact.put("phoneMasked", myCard.phoneMasked());
        myAsContact.put("memo", null);
        mapper.insertContact(myAsContact);

        return new ContactDto(contactId, str(other.get("displayName")), str(other.get("company")),
                str(other.get("title")), str(other.get("emailMasked")), str(other.get("phoneMasked")),
                null, null);
    }

    @Transactional(readOnly = true)
    public List<ContactDto> list(String userId) {
        return mapper.listContacts(userId).stream()
                .map(m -> new ContactDto(str(m.get("id")), str(m.get("displayName")), str(m.get("company")),
                        str(m.get("title")), str(m.get("emailMasked")), str(m.get("phoneMasked")),
                        str(m.get("memo")), str(m.get("savedAt"))))
                .toList();
    }

    @Transactional
    public void delete(String userId, String id) {
        if (mapper.deleteContact(userId, id) != 1) {
            throw new ApiException(ErrorCode.NOT_FOUND, "컨택트를 찾을 수 없습니다.");
        }
    }

    // ── helpers ──
    private static Map<String, Object> contactRow(String id, String owner, String contactUser, Map<String, Object> src) {
        Map<String, Object> row = new java.util.HashMap<>();
        row.put("id", id);
        row.put("ownerUserId", owner);
        row.put("contactUserId", contactUser);
        row.put("displayName", src.get("displayName"));
        row.put("company", src.get("company"));
        row.put("title", src.get("title"));
        row.put("emailMasked", src.get("emailMasked"));
        row.put("phoneMasked", src.get("phoneMasked"));
        row.put("memo", null);
        return row;
    }

    private static MyCardDto toCard(Map<String, Object> m) {
        String token = str(m.get("shareToken"));
        return new MyCardDto(str(m.get("displayName")), str(m.get("company")), str(m.get("title")),
                str(m.get("emailMasked")), str(m.get("phoneMasked")), token, QR_PREFIX + token);
    }

    private static String parseToken(String qrPayload) {
        if (qrPayload == null || qrPayload.isBlank()) {
            throw new ApiException(ErrorCode.VALIDATION, "QR 코드가 필요합니다.");
        }
        String p = qrPayload.trim();
        return p.startsWith(QR_PREFIX) ? p.substring(QR_PREFIX.length()) : p;
    }

    private String newShareToken() {
        return "SHARE-" + HexFormat.of().formatHex(randomBytes()).toUpperCase();
    }

    private byte[] randomBytes() {
        byte[] b = new byte[8];
        random.nextBytes(b);
        return b;
    }

    private static String blankToNull(String s) {
        return s == null || s.isBlank() ? null : s.trim();
    }

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