package com.cloudhandson.vpdbackoffice.service;

import com.cloudhandson.vpdbackoffice.domain.audit.AuditEvent;
import com.cloudhandson.vpdbackoffice.domain.group.AppGroup;
import com.cloudhandson.vpdbackoffice.domain.group.GroupCreateCommand;
import com.cloudhandson.vpdbackoffice.domain.group.GroupRoleView;
import com.cloudhandson.vpdbackoffice.domain.group.GroupUserView;
import com.cloudhandson.vpdbackoffice.mapper.GroupMapper;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class GroupService {

  private final GroupMapper groupMapper;
  private final AuditService auditService;
  private final ExternalAuthorizationChangeNotifier authorizationChangeNotifier;

  @Autowired
  public GroupService(
      GroupMapper groupMapper,
      AuditService auditService,
      ExternalAuthorizationChangeNotifier authorizationChangeNotifier
  ) {
    this.groupMapper = groupMapper;
    this.auditService = auditService;
    this.authorizationChangeNotifier = authorizationChangeNotifier;
  }

  public GroupService(GroupMapper groupMapper, AuditService auditService) {
    this(groupMapper, auditService, ExternalAuthorizationChangeNotifier.noop());
  }

  public List<AppGroup> findAll() {
    return groupMapper.findAll();
  }

  public List<GroupUserView> findGroupUsers() {
    return groupMapper.findGroupUsers();
  }

  public List<GroupRoleView> findGroupRoles() {
    return groupMapper.findGroupRoles();
  }

  @Transactional
  public void createGroup(GroupCreateCommand command) {
    long groupId = groupMapper.nextGroupId();
    groupMapper.insertGroup(groupId, command);
    auditService.record(new AuditEvent("GROUP_CREATED", null, null, "SUCCESS", null, null, command.groupCode()));
    authorizationChangeNotifier.changed("GROUP_CREATED");
  }

  @Transactional
  public void setActive(long groupId, boolean active) {
    setActive(groupId, active, true);
  }

  @Transactional
  public void setActive(long groupId, boolean active, boolean confirmImpact) {
    if (!active && !confirmImpact) {
      throw new AppException("그룹 비활성화 전 영향 확인이 필요합니다.");
    }
    int updated = groupMapper.updateActive(groupId, active ? "Y" : "N");
    if (updated == 0) {
      throw new AppException("그룹을 찾을 수 없습니다.");
    }
    auditService.record(new AuditEvent("GROUP_ACTIVE_CHANGED", null, null, "SUCCESS", null, null,
        "groupId=" + groupId + ",active=" + active));
    authorizationChangeNotifier.changed("GROUP_ACTIVE_CHANGED");
  }

  @Transactional
  public void addUser(long groupId, long userId) {
    groupMapper.insertGroupUser(groupId, userId);
    auditService.record(new AuditEvent("GROUP_USER_ADDED", null, null, "SUCCESS", null, null,
        "groupId=" + groupId + ",userId=" + userId));
    authorizationChangeNotifier.changed("GROUP_USER_ADDED");
  }

  @Transactional
  public void removeUser(long groupId, long userId) {
    removeUser(groupId, userId, true);
  }

  @Transactional
  public void removeUser(long groupId, long userId, boolean confirmImpact) {
    if (!confirmImpact) {
      throw new AppException("그룹 사용자 해제 전 영향 확인이 필요합니다.");
    }
    int deleted = groupMapper.deleteGroupUser(groupId, userId);
    if (deleted == 0) {
      throw new AppException("삭제할 그룹 사용자 매핑을 찾을 수 없습니다.");
    }
    auditService.record(new AuditEvent("GROUP_USER_REMOVED", null, null, "SUCCESS", null, null,
        "groupId=" + groupId + ",userId=" + userId));
    authorizationChangeNotifier.changed("GROUP_USER_REMOVED");
  }

  @Transactional
  public void addRole(long groupId, long roleId) {
    groupMapper.insertGroupRole(groupId, roleId);
    auditService.record(new AuditEvent("GROUP_ROLE_ADDED", null, null, "SUCCESS", null, null,
        "groupId=" + groupId + ",roleId=" + roleId));
    authorizationChangeNotifier.changed("GROUP_ROLE_ADDED");
  }

  @Transactional
  public void removeRole(long groupId, long roleId) {
    removeRole(groupId, roleId, true);
  }

  @Transactional
  public void removeRole(long groupId, long roleId, boolean confirmImpact) {
    if (!confirmImpact) {
      throw new AppException("그룹 역할 해제 전 영향 확인이 필요합니다.");
    }
    int deleted = groupMapper.deleteGroupRole(groupId, roleId);
    if (deleted == 0) {
      throw new AppException("삭제할 그룹 역할 매핑을 찾을 수 없습니다.");
    }
    auditService.record(new AuditEvent("GROUP_ROLE_REMOVED", null, null, "SUCCESS", null, null,
        "groupId=" + groupId + ",roleId=" + roleId));
    authorizationChangeNotifier.changed("GROUP_ROLE_REMOVED");
  }
}
