package com.davos.assessment.profile;

import com.davos.assessment.auth.UserPrincipal;
import com.davos.assessment.common.ApiException;
import com.davos.assessment.project.Project;
import com.davos.assessment.project.ProjectAccessService;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import java.time.DateTimeException;
import java.time.Instant;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.Set;
import java.util.regex.Pattern;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping({"/api/me/profile", "/api/projects/{slug}/me/profile"})
public class ProfileController {
  private static final Pattern NAME_PATTERN = Pattern.compile("^[\\u4e00-\\u9fa5A-Za-z·•]{2,30}$");
  private static final Pattern PHONE_PATTERN = Pattern.compile("^1[3-9]\\d{9}$");
  private static final Pattern IDCARD_PATTERN = Pattern.compile("^\\d{17}[\\dXx]$");
  private static final Set<String> EDUCATION_SET = Set.of("博士", "硕士", "本科", "大专", "高中及以下");
  private final ProfileRepository profileRepository;
  private final ProjectAccessService projectAccessService;

  public ProfileController(ProfileRepository profileRepository, ProjectAccessService projectAccessService) {
    this.profileRepository = profileRepository;
    this.projectAccessService = projectAccessService;
  }

  @GetMapping
  public ProfileResponse get(@AuthenticationPrincipal UserPrincipal principal) {
    Project project = projectAccessService.requireProjectParticipant(projectAccessService.currentSlugOrDefault(), principal);
    return profileRepository.findByProjectAndUser(project, principal.user()).map(ProfileResponse::from).orElse(null);
  }

  @PutMapping
  @Transactional
  public ProfileResponse upsert(@AuthenticationPrincipal UserPrincipal principal, @Valid @RequestBody ProfileRequest request) {
    Project project = projectAccessService.requireProjectParticipant(projectAccessService.currentSlugOrDefault(), principal);
    validateRequest(request);
    String normalizedIdCard = request.idCard().trim().toUpperCase();
    String birthDate = extractBirthDate(normalizedIdCard);
    Profile profile = profileRepository.findByProjectAndUser(project, principal.user()).orElseGet(Profile::new);
    profile.setProject(project);
    profile.setUser(principal.user());
    profile.setName(request.name().trim());
    profile.setGender(request.gender().trim());
    profile.setIdCard(normalizedIdCard);
    profile.setPhone(request.phone().trim());
    profile.setBirthMonth(birthDate);
    profile.setEducation(request.education().trim());
    profile.setSchoolMajor(request.schoolMajor().trim());
    profile.setOrganization(request.schoolMajor().trim());
    profile.setTitle(request.education().trim());
    profile.setIndustry("心理测评用户");
    profile.setCity("未填写");
    profile.setAttendeeType("参测用户");
    profile.setUpdatedAt(Instant.now());
    return ProfileResponse.from(profileRepository.save(profile));
  }

  public record ProfileRequest(
      @NotBlank String name,
      @NotBlank String gender,
      @NotBlank String idCard,
      @NotBlank String phone,
      @NotBlank String education,
      @NotBlank String schoolMajor) {}

  public record ProfileResponse(
      Long id,
      String name,
      String gender,
      String idCard,
      String phone,
      String birthMonth,
      String education,
      String schoolMajor) {
    static ProfileResponse from(Profile profile) {
      return new ProfileResponse(profile.getId(), profile.getName(), profile.getGender(), maskIdCard(profile.getIdCard()),
          maskPhone(profile.getPhone()), profile.getBirthMonth(), profile.getEducation(), profile.getSchoolMajor());
    }

    private static String maskPhone(String phone) {
      if (phone == null || phone.length() < 7) return phone;
      return phone.substring(0, 3) + "****" + phone.substring(phone.length() - 4);
    }

    private static String maskIdCard(String idCard) {
      if (idCard == null || idCard.length() < 8) return idCard;
      return idCard.substring(0, 4) + "**********" + idCard.substring(idCard.length() - 4);
    }
  }

  private void validateRequest(ProfileRequest request) {
    String name = request.name() == null ? "" : request.name().trim();
    String gender = request.gender() == null ? "" : request.gender().trim();
    String idCard = request.idCard() == null ? "" : request.idCard().trim();
    String phone = request.phone() == null ? "" : request.phone().trim();
    String education = request.education() == null ? "" : request.education().trim();
    String schoolMajor = request.schoolMajor() == null ? "" : request.schoolMajor().trim();

    if (!NAME_PATTERN.matcher(name).matches()) {
      throw new ApiException(HttpStatus.BAD_REQUEST, "姓名格式不正确，请填写真实姓名");
    }
    if (!("男".equals(gender) || "女".equals(gender) || "其他".equals(gender))) {
      throw new ApiException(HttpStatus.BAD_REQUEST, "性别信息不正确");
    }
    if (!IDCARD_PATTERN.matcher(idCard).matches() || !isValidChineseIdCard(idCard)) {
      throw new ApiException(HttpStatus.BAD_REQUEST, "身份证号格式不正确，请填写真实有效身份证号");
    }
    if (!PHONE_PATTERN.matcher(phone).matches()) {
      throw new ApiException(HttpStatus.BAD_REQUEST, "手机号格式不正确，请填写真实联系方式");
    }
    if (!EDUCATION_SET.contains(education)) {
      throw new ApiException(HttpStatus.BAD_REQUEST, "学历选项无效");
    }
    if (schoolMajor.length() < 2 || schoolMajor.length() > 120) {
      throw new ApiException(HttpStatus.BAD_REQUEST, "院校/专业信息不正确，请填写真实信息");
    }
  }

  private String extractBirthDate(String idCard) {
    try {
      String text = idCard.substring(6, 14);
      LocalDate date = LocalDate.parse(text, DateTimeFormatter.ofPattern("yyyyMMdd"));
      return date.toString();
    } catch (DateTimeException | IndexOutOfBoundsException ex) {
      throw new ApiException(HttpStatus.BAD_REQUEST, "身份证号中的出生日期无效");
    }
  }

  private boolean isValidChineseIdCard(String idCard) {
    String upper = idCard.toUpperCase();
    int[] weight = {7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
    char[] check = {'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};
    int sum = 0;
    for (int i = 0; i < 17; i++) {
      char ch = upper.charAt(i);
      if (ch < '0' || ch > '9') return false;
      sum += (ch - '0') * weight[i];
    }
    return check[sum % 11] == upper.charAt(17);
  }
}
