package com.malk.benteler.service;

import com.malk.benteler.config.BentelerYidaConf;
import com.malk.benteler.dto.EiamBatchItemResult;
import com.malk.benteler.dto.EiamBatchStage;
import com.malk.benteler.dto.EiamCreateUserItem;
import com.malk.benteler.dto.EiamUpdateUserItem;
import com.malk.server.eiam.EiamCustomField;
import com.malk.server.eiam.EiamCustomFieldUpdate;
import org.junit.Before;
import org.junit.Test;

import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

/**
 * {@link BentelerYidaFormMapper} 字段映射和子表全量重建测试。
 */
public class BentelerYidaFormMapperTest {

    private BentelerYidaConf conf;
    private BentelerYidaFormMapper mapper;

    @Before
    public void setUp() {
        conf = TestBentelerYidaConf.create();
        mapper = new BentelerYidaFormMapper(conf);
    }

    @Test
    public void mapCreateItems_readsOrganizationalUnitIdAndPlainPhone() {
        Map<String, Object> formData = new HashMap<>();
        formData.put(conf.getOnboardingOrganizationalUnitIdFieldId(), "ou_technical");
        formData.put(conf.getOnboardingCompanyFieldId(), "测试公司");
        formData.put(conf.getOnboardingCompanyCodeFieldId(), "COMPANY-01");
        formData.put(conf.getOnboardingDepartmentFieldId(), "Technical accounts");
        Map<String, Object> row = new HashMap<>();
        row.put(conf.getOnboardingNameFieldId(), "测试人员");
        row.put(conf.getOnboardingPhoneFieldId(), new BigDecimal("13800000001"));
        row.put(conf.getOnboardingJobTitleFieldId(), "Software Engineer");
        row.put(conf.getOnboardingManagerFieldId() + "_id",
                Collections.singletonList("manager_1"));

        EiamCreateUserItem item = mapper.mapCreateItems("form_1", formData,
                Collections.singletonList(row)).get(0);

        assertEquals("ou_technical", item.getPrimaryOrganizationalUnitId());
        assertEquals("13800000001", item.getUsername());
        assertEquals("manager_1", customFieldValue(item, "direct_line_manager"));
        assertEquals("测试公司", customFieldValue(item, "company_name"));
        assertEquals("COMPANY-01", customFieldValue(item, "company_code"));
        assertEquals("Software Engineer", customFieldValue(item, "job_title"));
        assertEquals("Technical accounts", customFieldValue(item, "department"));
    }

    @Test
    public void rebuildRows_writesEmployeeIdsToBaseFieldAndPreservesAllRows() {
        Map<String, Object> first = new HashMap<>();
        first.put(conf.getUpdateEmployeeFieldId(), "显示姓名");
        first.put(conf.getUpdateEmployeeFieldId() + "_id", Arrays.asList("user_1"));
        first.put(conf.getUpdatePhoneFieldId(), "17612168216");
        first.put(conf.getUpdateEmployeeNumberFieldId(), "EMP-001");
        first.put(conf.getUpdateJobTitleFieldId(), "Software Engineer");
        first.put(conf.getUpdateDescriptionFieldId(), "说明一");
        Map<String, Object> second = new HashMap<>();
        second.put(conf.getUpdateEmployeeFieldId() + "_id", Arrays.asList("user_2"));
        List<EiamBatchItemResult> results = Arrays.asList(result(true), result(false));

        List<Map<String, Object>> rebuilt = mapper.rebuildRows(conf.getUpdateTableFieldId(),
                Arrays.asList(first, second), results);

        assertEquals(2, rebuilt.size());
        assertEquals(Arrays.asList("user_1"), rebuilt.get(0).get(conf.getUpdateEmployeeFieldId()));
        assertEquals("17612168216", rebuilt.get(0).get(conf.getUpdatePhoneFieldId()));
        assertEquals("EMP-001", rebuilt.get(0).get(conf.getUpdateEmployeeNumberFieldId()));
        assertEquals("Software Engineer", rebuilt.get(0).get(conf.getUpdateJobTitleFieldId()));
        assertFalse(rebuilt.get(0).containsKey(conf.getUpdateEmployeeFieldId() + "_id"));
        assertEquals("成功", rebuilt.get(0).get(conf.getDetailStatusFieldId()));
        assertEquals("失败", rebuilt.get(1).get(conf.getDetailStatusFieldId()));
    }

    @Test
    public void buildFailureMessage_usesPersonNameAndClearsWhenAllSuccess() {
        Map<String, Object> first = new HashMap<>();
        first.put(conf.getOnboardingNameFieldId(), "成功人员");
        Map<String, Object> second = new HashMap<>();
        second.put(conf.getOnboardingNameFieldId(), "失败人员");
        EiamBatchItemResult failed = EiamBatchItemResult.builder().success(false)
                .message("部门不存在").build();

        String message = mapper.buildFailureMessage(conf.getOnboardingTableFieldId(),
                Arrays.asList(first, second), Arrays.asList(result(true), failed));
        String successMessage = mapper.buildFailureMessage(conf.getOnboardingTableFieldId(),
                Collections.singletonList(first), Collections.singletonList(result(true)));

        assertEquals("失败人员 - 部门不存在", message);
        assertEquals("", successMessage);
    }

    @Test
    public void mapUpdateItems_addDepartmentAndPatch_mapsOnlyRequestedOperations() {
        Map<String, Object> row = new HashMap<>();
        row.put(conf.getUpdateEmployeeFieldId() + "_id", Collections.singletonList("user_1"));
        row.put(conf.getUpdatePhoneFieldId(), "17612168216");
        row.put(conf.getUpdateEmployeeNumberFieldId(), "EMP-001");
        row.put(conf.getUpdateJobTitleFieldId(), "Software Engineer");
        List<String> updateContents = Arrays.asList(BentelerYidaFormMapper.ADD_ORG,
                BentelerYidaFormMapper.UPDATE_PHONE,
                BentelerYidaFormMapper.UPDATE_EMPLOYEE_NUMBER,
                BentelerYidaFormMapper.UPDATE_JOB_TITLE);

        EiamUpdateUserItem item = mapper.mapUpdateItems("form_update",
                Collections.singletonList(row), "ou_target", updateContents).get(0);

        assertEquals("user_1", item.getUserId());
        assertEquals(null, item.getPrimaryOrganizationalUnitId());
        assertEquals("ou_target", item.getAdditionalOrganizationalUnitId());
        assertEquals("17612168216", item.getUsername());
        assertEquals("17612168216", item.getPhoneNumber());
        assertEquals("86", item.getPhoneRegion());
        assertEquals("EMP-001", customFieldValue(item, "employee_id_number"));
        assertEquals("Software Engineer", customFieldValue(item, "job_title"));
    }

    @Test
    public void mapUpdateItems_departmentOnly_ignoresPopulatedPatchFields() {
        Map<String, Object> row = new HashMap<>();
        row.put(conf.getUpdateEmployeeFieldId() + "_id", Collections.singletonList("user_1"));
        row.put(conf.getUpdatePhoneFieldId(), "17612168216");
        row.put(conf.getUpdateEmployeeNumberFieldId(), "EMP-001");
        row.put(conf.getUpdateJobTitleFieldId(), "Software Engineer");

        EiamUpdateUserItem item = mapper.mapUpdateItems("form_update",
                Collections.singletonList(row), "ou_target",
                Collections.singletonList(BentelerYidaFormMapper.REPLACE_PRIMARY_ORG)).get(0);

        assertEquals("ou_target", item.getPrimaryOrganizationalUnitId());
        assertEquals(null, item.getAdditionalOrganizationalUnitId());
        assertEquals(null, item.getPhoneNumber());
        assertEquals(null, item.getCustomFields());
    }

    private EiamBatchItemResult result(boolean success) {
        return EiamBatchItemResult.builder().success(success).stage(EiamBatchStage.COMPLETED)
                .build();
    }

    private String customFieldValue(EiamCreateUserItem item, String fieldName) {
        for (EiamCustomField field : item.getCustomFields()) {
            if (fieldName.equals(field.getFieldName())) {
                return field.getFieldValue();
            }
        }
        return null;
    }

    private String customFieldValue(EiamUpdateUserItem item, String fieldName) {
        if (item.getCustomFields() == null) {
            return null;
        }
        for (EiamCustomFieldUpdate field : item.getCustomFields()) {
            if (fieldName.equals(field.getFieldName())) {
                assertEquals("replace", field.getOperation());
                return field.getFieldValue();
            }
        }
        return null;
    }
}
