package com.yaoai.api.service;

import cn.hutool.crypto.digest.BCrypt;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yaoai.api.dto.onboarding.TenantOnboardingRequest;
import com.yaoai.common.exception.BizException;
import com.yaoai.common.exception.ErrorCode;
import com.yaoai.domain.entity.Plan;
import com.yaoai.domain.entity.Tenant;
import com.yaoai.domain.entity.TenantBalance;
import com.yaoai.domain.entity.TenantMember;
import com.yaoai.domain.entity.TenantQuota;
import com.yaoai.domain.entity.User;
import com.yaoai.domain.mapper.PlanMapper;
import com.yaoai.domain.mapper.TenantBalanceMapper;
import com.yaoai.domain.mapper.TenantMapper;
import com.yaoai.domain.mapper.TenantMemberMapper;
import com.yaoai.domain.mapper.TenantQuotaMapper;
import com.yaoai.domain.mapper.UserMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;

@Service
@RequiredArgsConstructor
public class TenantOnboardingService {

    private final JdbcTemplate jdbcTemplate;
    private final TenantMapper tenantMapper;
    private final UserMapper userMapper;
    private final TenantMemberMapper tenantMemberMapper;
    private final PlanMapper planMapper;
    private final TenantBalanceMapper tenantBalanceMapper;
    private final TenantQuotaMapper tenantQuotaMapper;

    @Transactional
    public Map<String, Object> submit(
            TenantOnboardingRequest request,
            MultipartFile businessLicense,
            MultipartFile legalIdentity,
            MultipartFile authorizationLetter) {
        ensureTable();
        String creditCode = cleanUpper(request.getCreditCode());
        String ownerEmail = request.getOwnerEmail().trim().toLowerCase();

        if (userMapper.existsByEmail(ownerEmail)) {
            throw new BizException(ErrorCode.INVALID_PARAM, "Owner 邮箱已存在，请更换邮箱或直接登录");
        }
        if (existsApplicationByCreditCode(creditCode)) {
            throw new BizException(ErrorCode.INVALID_PARAM, "该企业已提交入驻申请，请勿重复提交");
        }
        if (existsApplicationByOwnerEmail(ownerEmail)) {
            throw new BizException(ErrorCode.INVALID_PARAM, "该 Owner 邮箱已有待审核入驻申请");
        }

        String planCode = planCode(request.getTrialPlan());
        Plan plan = planMapper.selectOne(Wrappers.<Plan>lambdaQuery()
                .eq(Plan::getCode, planCode)
                .eq(Plan::getIsActive, 1));
        if (plan == null) {
            throw new BizException(ErrorCode.INVALID_PARAM, "拟开通套餐不存在或已停用");
        }

        LocalDateTime now = LocalDateTime.now();
        Tenant tenant = new Tenant();
        tenant.setName(request.getCompanyName().trim());
        tenant.setSlug(uniqueSlug(creditCode));
        tenant.setPlan(planCode.toLowerCase());
        tenant.setStatus(0);
        tenant.setCredits(BigDecimal.ZERO);
        tenant.setFrozenCredits(BigDecimal.ZERO);
        tenant.setNotes("企业入驻待审核：" + creditCode);
        tenant.setCreatedAt(now);
        tenant.setUpdatedAt(now);
        tenantMapper.insert(tenant);

        User owner = new User();
        owner.setTenantId(tenant.getId());
        owner.setUsername(request.getOwnerUsername().trim());
        owner.setEmail(ownerEmail);
        owner.setPhone(clean(request.getContactPhone()));
        owner.setPasswordHash(BCrypt.hashpw(request.getOwnerPassword(), BCrypt.gensalt()));
        owner.setRole("owner");
        owner.setStatus(0);
        owner.setCreatedAt(now);
        owner.setUpdatedAt(now);
        userMapper.insert(owner);

        TenantMember member = new TenantMember();
        member.setTenantId(tenant.getId());
        member.setUserId(owner.getId());
        member.setRole("owner");
        member.setPosition("企业管理员");
        member.setJoinedAt(now);
        tenantMemberMapper.insert(member);

        TenantBalance balance = new TenantBalance();
        balance.setTenantId(tenant.getId());
        balance.setBalance(BigDecimal.ZERO);
        balance.setTotalRecharged(BigDecimal.ZERO);
        balance.setTotalSpent(BigDecimal.ZERO);
        balance.setVersion(0);
        balance.setUpdatedAt(now);
        tenantBalanceMapper.insert(balance);

        TenantQuota quota = new TenantQuota();
        quota.setTenantId(tenant.getId());
        quota.setMaxMembers(plan.getMaxMembers());
        quota.setMaxProjects(plan.getMaxProjects());
        quota.setMaxMonthlyCredits(plan.getMonthlyCredits());
        quota.setMaxConcurrentTasks(plan.getMaxConcurrentTasks());
        quota.setNotes("入驻审核通过后按 " + plan.getName() + " 试用开通");
        quota.setUpdatedAt(now);
        tenantQuotaMapper.insert(quota);

        String licenseFile = saveFile(businessLicense, "business-license");
        String identityFile = saveFile(legalIdentity, "legal-identity");
        String authorizationFile = saveFile(authorizationLetter, "authorization");
        boolean hasLicense = StringUtils.hasText(licenseFile);
        boolean hasIdentity = StringUtils.hasText(identityFile);
        boolean needsAuthorization = StringUtils.hasText(request.getAgentName())
                && !request.getAgentName().trim().equals(request.getLegalPersonName().trim());
        boolean hasAuthorization = StringUtils.hasText(authorizationFile);
        String status = hasLicense && hasIdentity && (!needsAuthorization || hasAuthorization) ? "资料待审" : "待补件";
        String risk = "资料待审".equals(status) ? "低风险" : "需补件";
        String riskReason = riskReason(hasLicense, hasIdentity, needsAuthorization, hasAuthorization);
        long applicationId = newId();

        jdbcTemplate.update("""
                insert into tenant_onboarding_applications
                (id, tenant_id, owner_user_id, company_name, credit_code, legal_person_name, agent_name,
                 contact_name, contact_phone, contact_email, owner_username, owner_email, industry, company_scale,
                 business_scope, trial_plan, license_file, identity_file, authorization_file, license_status,
                 identity_status, authorization_status, risk_level, risk_reason, status, source, created_at, updated_at)
                values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '客户端入驻', ?, ?)
                """,
                applicationId,
                tenant.getId(),
                owner.getId(),
                request.getCompanyName().trim(),
                creditCode,
                request.getLegalPersonName().trim(),
                clean(request.getAgentName()),
                request.getContactName().trim(),
                clean(request.getContactPhone()),
                clean(request.getContactEmail()),
                request.getOwnerUsername().trim(),
                ownerEmail,
                clean(request.getIndustry()),
                clean(request.getCompanyScale()),
                clean(request.getBusinessScope()),
                trialPlanLabel(planCode),
                licenseFile,
                identityFile,
                authorizationFile,
                hasLicense ? "待验真" : "待补件",
                hasIdentity ? "待核身" : "待补件",
                needsAuthorization ? (hasAuthorization ? "待确认" : "待补件") : "无需授权",
                risk,
                riskReason,
                status,
                now,
                now);

        Map<String, Object> result = new LinkedHashMap<>();
        result.put("applicationId", applicationId);
        result.put("tenantId", tenant.getId());
        result.put("status", status);
        result.put("riskLevel", risk);
        result.put("message", "入驻申请已提交，运营审核通过后 Owner 账号会被激活");
        return result;
    }

    public Map<String, Object> getApplication(Long id) {
        ensureTable();
        return jdbcTemplate.query(
                "select id, company_name, credit_code, status, risk_level, risk_reason, created_at, reviewed_at from tenant_onboarding_applications where id = ?",
                rs -> {
                    if (!rs.next()) {
                        throw new BizException(ErrorCode.NOT_FOUND, "入驻申请不存在");
                    }
                    Map<String, Object> result = new LinkedHashMap<>();
                    result.put("applicationId", rs.getLong("id"));
                    result.put("companyName", rs.getString("company_name"));
                    result.put("creditCode", rs.getString("credit_code"));
                    result.put("status", rs.getString("status"));
                    result.put("riskLevel", rs.getString("risk_level"));
                    result.put("riskReason", rs.getString("risk_reason"));
                    result.put("createdAt", rs.getTimestamp("created_at").toLocalDateTime().toString());
                    result.put("reviewedAt", rs.getTimestamp("reviewed_at") == null ? null : rs.getTimestamp("reviewed_at").toLocalDateTime().toString());
                    return result;
                },
                id);
    }

    private boolean existsApplicationByCreditCode(String creditCode) {
        Long count = jdbcTemplate.queryForObject(
                "select count(*) from tenant_onboarding_applications where credit_code = ? and status not in ('已驳回')",
                Long.class,
                creditCode);
        return count != null && count > 0;
    }

    private boolean existsApplicationByOwnerEmail(String ownerEmail) {
        Long count = jdbcTemplate.queryForObject(
                "select count(*) from tenant_onboarding_applications where owner_email = ? and status not in ('已驳回')",
                Long.class,
                ownerEmail);
        return count != null && count > 0;
    }

    private String saveFile(MultipartFile file, String category) {
        if (file == null || file.isEmpty()) {
            return "";
        }
        try {
            String originalName = StringUtils.cleanPath(file.getOriginalFilename() == null ? category : file.getOriginalFilename());
            String ext = "";
            int dotIndex = originalName.lastIndexOf('.');
            if (dotIndex >= 0) {
                ext = originalName.substring(dotIndex);
            }
            String storedName = UUID.randomUUID() + ext;
            Path uploadDir = Path.of(".local-data", "tenant-onboarding", category).toAbsolutePath().normalize();
            Files.createDirectories(uploadDir);
            Path target = uploadDir.resolve(storedName);
            Files.copy(file.getInputStream(), target, StandardCopyOption.REPLACE_EXISTING);
            return originalName + "（已上传：" + target + "）";
        } catch (Exception e) {
            throw new BizException(ErrorCode.UPLOAD_FAILED, "入驻材料上传失败");
        }
    }

    private String uniqueSlug(String creditCode) {
        String base = "ent-" + creditCode.toLowerCase();
        if (base.length() > 42) {
            base = base.substring(0, 42);
        }
        String slug = base;
        int index = 1;
        while (tenantMapper.selectCount(Wrappers.<Tenant>lambdaQuery().eq(Tenant::getSlug, slug)) > 0) {
            slug = base + "-" + index++;
        }
        return slug;
    }

    private String planCode(String value) {
        if (!StringUtils.hasText(value)) {
            return "PRO";
        }
        String normalized = value.trim().toUpperCase();
        if (normalized.contains("TEAM") || normalized.contains("团队")) return "TEAM";
        if (normalized.contains("ENTERPRISE") || normalized.contains("企业")) return "ENTERPRISE";
        if (normalized.contains("FREE") || normalized.contains("免费")) return "FREE";
        return "PRO";
    }

    private String trialPlanLabel(String planCode) {
        return switch (planCode) {
            case "FREE" -> "免费版试用";
            case "TEAM" -> "团队版试用";
            case "ENTERPRISE" -> "企业版试用";
            default -> "专业版试用";
        };
    }

    private String riskReason(boolean hasLicense, boolean hasIdentity, boolean needsAuthorization, boolean hasAuthorization) {
        if (hasLicense && hasIdentity && (!needsAuthorization || hasAuthorization)) {
            return "证照、法人身份与授权材料已提交，待运营验真";
        }
        StringBuilder builder = new StringBuilder();
        if (!hasLicense) builder.append("缺营业执照；");
        if (!hasIdentity) builder.append("缺法人身份核验材料；");
        if (needsAuthorization && !hasAuthorization) builder.append("经办人非法人，缺授权书；");
        return builder.toString();
    }

    private String clean(String value) {
        return StringUtils.hasText(value) ? value.trim() : "";
    }

    private String cleanUpper(String value) {
        return clean(value).toUpperCase();
    }

    private long newId() {
        return System.currentTimeMillis() * 1000 + Math.abs(UUID.randomUUID().getLeastSignificantBits() % 1000);
    }

    private void ensureTable() {
        jdbcTemplate.execute("""
                create table if not exists tenant_onboarding_applications
                (
                    id bigint not null primary key,
                    tenant_id bigint,
                    owner_user_id bigint,
                    company_name varchar(160) not null,
                    credit_code varchar(32) not null,
                    legal_person_name varchar(80) not null,
                    agent_name varchar(80),
                    contact_name varchar(80) not null,
                    contact_phone varchar(40),
                    contact_email varchar(160),
                    owner_username varchar(64) not null,
                    owner_email varchar(160) not null,
                    industry varchar(80),
                    company_scale varchar(40),
                    business_scope text,
                    trial_plan varchar(40) not null default 'PRO_TRIAL',
                    license_file varchar(500),
                    identity_file varchar(500),
                    authorization_file varchar(500),
                    license_status varchar(40) not null default '待验真',
                    identity_status varchar(40) not null default '待核身',
                    authorization_status varchar(40) not null default '待确认',
                    risk_level varchar(40) not null default '需人工确认',
                    risk_reason varchar(255),
                    status varchar(40) not null default '资料待审',
                    review_note text,
                    reviewed_by bigint,
                    reviewed_at datetime(3),
                    source varchar(40) not null default '客户端入驻',
                    created_at datetime(3) not null default current_timestamp(3),
                    updated_at datetime(3) not null default current_timestamp(3)
                )
                """);
    }
}
