package com.davos.assessment.config;

import com.davos.assessment.assessment.*;
import com.davos.assessment.invitation.*;
import com.davos.assessment.project.*;
import com.davos.assessment.user.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.*;
import org.springframework.core.io.ClassPathResource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
public class DataSeeder implements ApplicationRunner {
  private final UserRepository userRepository;
  private final InvitationCodeRepository invitationCodeRepository;
  private final AssessmentRepository assessmentRepository;
  private final QuestionRepository questionRepository;
  private final AnswerRepository answerRepository;
  private final ProjectRepository projectRepository;
  private final ProjectMembershipRepository projectMembershipRepository;
  private final ProjectAssessmentRepository projectAssessmentRepository;
  private final JdbcTemplate jdbcTemplate;
  private final PasswordEncoder passwordEncoder;
  private final String adminEmail;
  private final String adminPassword;
  private final String seedInviteCode;
  private final boolean seedAdminEnabled;
  private final boolean seedInviteEnabled;
  private final Set<String> allowedActiveAssessments;

  public DataSeeder(
      UserRepository userRepository,
      InvitationCodeRepository invitationCodeRepository,
      AssessmentRepository assessmentRepository,
      QuestionRepository questionRepository,
      AnswerRepository answerRepository,
      ProjectRepository projectRepository,
      ProjectMembershipRepository projectMembershipRepository,
      ProjectAssessmentRepository projectAssessmentRepository,
      JdbcTemplate jdbcTemplate,
      PasswordEncoder passwordEncoder,
      @Value("${app.admin.email}") String adminEmail,
      @Value("${app.admin.password}") String adminPassword,
      @Value("${app.invite.seed-code}") String seedInviteCode,
      @Value("${app.seed.admin-enabled:true}") boolean seedAdminEnabled,
      @Value("${app.seed.invite-enabled:true}") boolean seedInviteEnabled,
      @Value("${app.lock.allowed-active-assessments:SCL90-2078,SDS-2082,SAS-2083}") String allowedActiveAssessments) {
    this.userRepository = userRepository;
    this.invitationCodeRepository = invitationCodeRepository;
    this.assessmentRepository = assessmentRepository;
    this.questionRepository = questionRepository;
    this.answerRepository = answerRepository;
    this.projectRepository = projectRepository;
    this.projectMembershipRepository = projectMembershipRepository;
    this.projectAssessmentRepository = projectAssessmentRepository;
    this.jdbcTemplate = jdbcTemplate;
    this.passwordEncoder = passwordEncoder;
    this.adminEmail = adminEmail;
    this.adminPassword = adminPassword;
    this.seedInviteCode = seedInviteCode;
    this.seedAdminEnabled = seedAdminEnabled;
    this.seedInviteEnabled = seedInviteEnabled;
    this.allowedActiveAssessments = parseCsvSet(allowedActiveAssessments);
  }

  @Override
  @Transactional
  public void run(ApplicationArguments args) {
    lockSeeder();
    Project defaultProject = seedDefaultProject();
    if (seedAdminEnabled) {
      AppUser admin = seedAdmin();
      seedMembership(defaultProject, admin, ProjectMembershipRole.PROJECT_ADMIN);
    }
    if (seedInviteEnabled) {
      seedInvite(defaultProject);
    }

    syncAssessment("SCL90-2078", "SCL-90 症状自评量表", "90 项心理症状筛查量表，包含十个症状因子。", "question-bank/SCL90-2078.csv");
    syncAssessment("SDS-2082", "SDS 抑郁自评量表", "20 项抑郁自评筛查量表。", "question-bank/SDS-2082.csv");
    syncAssessment("SAS-2083", "SAS 焦虑自评量表", "20 项焦虑自评筛查量表。", "question-bank/SAS-2083.csv");
    lockAssessmentActivation();
    syncDefaultProjectAssessments(defaultProject);
  }

  private void lockSeeder() {
    jdbcTemplate.query(
        "select pg_advisory_xact_lock(?, ?)",
        ps -> {
          ps.setInt(1, 20260508);
          ps.setInt(2, 1);
        },
        rs -> null);
  }

  private Project seedDefaultProject() {
    return projectRepository.findBySlug(ProjectAccessService.DEFAULT_PROJECT_SLUG).orElseGet(() -> {
      Project project = new Project();
      project.setSlug(ProjectAccessService.DEFAULT_PROJECT_SLUG);
      project.setName("达沃斯会议心理测评系统");
      project.setLandingTitle("达沃斯心理测评");
      project.setLandingDescription("围绕国际会议高密度议程与高强度互动场景，提供面向参会人员的心理状态自评、压力识别与支持建议。");
      project.setReportTitle("达沃斯会议心理测评报告");
      project.setLogoUrl("");
      project.setEnabled(true);
      return projectRepository.save(project);
    });
  }

  private AppUser seedAdmin() {
    AppUser admin = userRepository.findByEmailIgnoreCase(adminEmail).orElseGet(() -> {
      AppUser item = new AppUser();
      item.setEmail(adminEmail.toLowerCase());
      item.setPasswordHash(passwordEncoder.encode(adminPassword));
      item.setRole(UserRole.PLATFORM_SUPER_ADMIN);
      return item;
    });
    if (admin.getRole() == UserRole.SUPER_ADMIN) {
      admin.setRole(UserRole.PLATFORM_SUPER_ADMIN);
    }
    return userRepository.save(admin);
  }

  private void seedMembership(Project project, AppUser user, ProjectMembershipRole role) {
    projectMembershipRepository.findByProjectAndUser(project, user).orElseGet(() -> {
      ProjectMembership membership = new ProjectMembership();
      membership.setProject(project);
      membership.setUser(user);
      membership.setRole(role);
      membership.setEnabled(true);
      return projectMembershipRepository.save(membership);
    });
  }

  private void seedInvite(Project project) {
    if (!invitationCodeRepository.existsByProjectAndCodeIgnoreCase(project, seedInviteCode)) {
      InvitationCode code = new InvitationCode();
      code.setCode(seedInviteCode.toUpperCase());
      code.setProject(project);
      code.setMaxUses(500);
      invitationCodeRepository.save(code);
    }
  }

  private void lockAssessmentActivation() {
    List<Assessment> all = assessmentRepository.findAll();
    for (Assessment item : all) {
      boolean active = allowedActiveAssessments.contains(item.getTestPaperId());
      if (item.isActive() != active) {
        item.setActive(active);
        assessmentRepository.save(item);
      }
    }
  }

  private void syncDefaultProjectAssessments(Project project) {
    for (Assessment assessment : assessmentRepository.findAll()) {
      boolean active = allowedActiveAssessments.contains(assessment.getTestPaperId());
      ProjectAssessment relation = projectAssessmentRepository.findByProjectAndAssessment(project, assessment).orElseGet(() -> {
        ProjectAssessment item = new ProjectAssessment();
        item.setProject(project);
        item.setAssessment(assessment);
        return item;
      });
      relation.setActive(active);
      projectAssessmentRepository.save(relation);
    }
  }

  private void syncAssessment(String testPaperId, String title, String description, String csvPath) {
    Assessment assessment = assessmentRepository.findByTestPaperId(testPaperId).orElseGet(() -> {
      Assessment item = new Assessment();
      item.setTestPaperId(testPaperId);
      item.setTitle(title);
      item.setDescription(description);
      item.setActive(true);
      return assessmentRepository.save(item);
    });
    assessment.setTitle(title);
    assessment.setDescription(description);
    assessment.setActive(true);
    assessmentRepository.save(assessment);

    List<QuestionCsvRow> csvRows = readCsv(csvPath);
    Set<String> csvCodes = new HashSet<>();
    for (QuestionCsvRow row : csvRows) {
      csvCodes.add(row.code());
      Question question = questionRepository.findByCode(row.code()).orElseGet(Question::new);
      question.setAssessment(assessment);
      question.setCode(row.code());
      question.setDimension(row.dimension());
      question.setPrompt(row.prompt());
      question.setSortOrder(row.sortOrder());
      questionRepository.save(question);
    }

    List<Question> existing = questionRepository.findByAssessmentOrderBySortOrderAsc(assessment);
    for (Question question : existing) {
      if (csvCodes.contains(question.getCode())) continue;
      if (answerRepository.countByQuestion(question) == 0) {
        questionRepository.delete(question);
      }
    }
  }

  private List<QuestionCsvRow> readCsv(String classpathFile) {
    try (BufferedReader reader = new BufferedReader(new InputStreamReader(new ClassPathResource(classpathFile).getInputStream(), StandardCharsets.UTF_8))) {
      List<QuestionCsvRow> rows = new ArrayList<>();
      String line = reader.readLine(); // header
      if (line == null) return rows;
      while ((line = reader.readLine()) != null) {
        if (line.isBlank()) continue;
        List<String> cells = parseCsvLine(line);
        if (cells.size() < 6) continue;
        rows.add(new QuestionCsvRow(cells.get(0).trim(), cells.get(1).trim(), cells.get(4).trim(), Integer.parseInt(cells.get(5).trim())));
      }
      return rows;
    } catch (IOException e) {
      throw new IllegalStateException("读取题库文件失败: " + classpathFile, e);
    }
  }

  private List<String> parseCsvLine(String line) {
    List<String> out = new ArrayList<>();
    StringBuilder cell = new StringBuilder();
    boolean quoted = false;
    for (int i = 0; i < line.length(); i++) {
      char ch = line.charAt(i);
      if (ch == '"') {
        if (quoted && i + 1 < line.length() && line.charAt(i + 1) == '"') {
          cell.append('"');
          i++;
        } else {
          quoted = !quoted;
        }
      } else if (ch == ',' && !quoted) {
        out.add(cell.toString());
        cell.setLength(0);
      } else {
        cell.append(ch);
      }
    }
    out.add(cell.toString());
    return out;
  }

  private String scl90Dimension(int i) {
    if (in(i, 1,4,12,27,40,42,48,49,52,53,56,58)) return "躯体化";
    if (in(i, 3,9,10,28,38,45,46,51,55,65)) return "强迫症状";
    if (in(i, 6,21,34,36,37,41,61,69,73)) return "人际关系敏感";
    if (in(i, 5,14,15,20,22,26,29,30,31,32,54,71,79)) return "抑郁";
    if (in(i, 2,17,23,33,39,57,72,78,80,86)) return "焦虑";
    if (in(i, 11,24,63,67,74,81)) return "敌对";
    if (in(i, 13,25,47,50,70,75,82)) return "恐怖";
    if (in(i, 8,18,43,68,76,83)) return "偏执";
    if (in(i, 7,16,35,62,77,84,85,87,88,90)) return "精神病性";
    return "其他";
  }

  private boolean in(int value, int... values) {
    for (int item : values) if (item == value) return true;
    return false;
  }

  private Set<String> parseCsvSet(String input) {
    Set<String> set = new HashSet<>();
    if (input == null || input.isBlank()) return set;
    for (String item : input.split(",")) {
      String val = item.trim();
      if (!val.isBlank()) set.add(val);
    }
    return set;
  }

  private record QuestionCsvRow(String code, String prompt, String dimension, int sortOrder) {}
}
