package de.votian.services.service;

import de.votian.services.config.ParameterKeys;
import de.votian.services.entity.CostCenter;
import de.votian.services.entity.Courier;
import de.votian.services.entity.Customer;
import de.votian.services.entity.Employee;
import de.votian.services.entity.User;
import de.votian.services.repository.CostCenterRepository;
import de.votian.services.repository.CourierRepository;
import de.votian.services.repository.CustomerRepository;
import de.votian.services.repository.UserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;

@Service
public class LegacyXmlB2BAuthService {

    private static final Logger log = LoggerFactory.getLogger(LegacyXmlB2BAuthService.class);
    private static final List<String> SESSION_QUERIES = List.of(
            "SELECT COUNT(*) FROM meta_object.metaobject " +
                    "WHERE mo_obj_type = 'usr' AND mo_id_ref_db = ? AND mo_obj_id = ? AND mo_hash = ?",
            "SELECT COUNT(*) FROM metaobject " +
                    "WHERE mo_obj_type = 'usr' AND mo_id_ref_db = ? AND mo_obj_id = ? AND mo_hash = ?"
    );

    private final LegacyXmlCustomerAuthService customerAuthService;
    private final CustomerRepository customerRepository;
    private final UserRepository userRepository;
    private final CourierRepository courierRepository;
    private final CostCenterRepository costCenterRepository;
    private final JdbcTemplate jdbcTemplate;
    private final ParameterService parameterService;

    public LegacyXmlB2BAuthService(LegacyXmlCustomerAuthService customerAuthService,
                                   CustomerRepository customerRepository,
                                   UserRepository userRepository,
                                   CourierRepository courierRepository,
                                   CostCenterRepository costCenterRepository,
                                   JdbcTemplate jdbcTemplate,
                                   ParameterService parameterService) {
        this.customerAuthService = customerAuthService;
        this.customerRepository = customerRepository;
        this.userRepository = userRepository;
        this.courierRepository = courierRepository;
        this.costCenterRepository = costCenterRepository;
        this.jdbcTemplate = jdbcTemplate;
        this.parameterService = parameterService;
    }

    public AuthContext authenticate(AuthRequest request) {
        if (request == null || isBlank(request.customerEid())) {
            throw new LegacyXmlRequestException("101", "Die Authentifizierungsdaten sind nicht in Ordnung.");
        }

        String authType = request.authType() == null || request.authType().isBlank()
                ? "cs"
                : request.authType().trim().toLowerCase();

        if ("cs".equals(authType)) {
            LegacyXmlCustomerAuthService.AuthContext customerContext = customerAuthService.authenticate(
                    new LegacyXmlCustomerAuthService.AuthRequest(
                            request.authType(),
                            request.customerEid(),
                            request.account(),
                            request.password(),
                            request.sessionId(),
                            request.costCenterName()
                    )
            );
            return new AuthContext(
                    customerContext.customer(),
                    customerContext.user(),
                    customerContext.employee(),
                    null,
                    customerContext.requestedCostCenter(),
                    customerContext.defaultCostCenter()
            );
        }

        if (!"cr".equals(authType)
                || isBlank(request.account())
                || isBlank(request.password())
                || isBlank(request.sessionId())) {
            throw new LegacyXmlRequestException("101", "Die Authentifizierungsdaten sind nicht in Ordnung.");
        }

        Customer customer = customerRepository.findByEid(request.customerEid().trim())
                .orElseThrow(() -> new LegacyXmlRequestException("101", "Die Authentifizierungsdaten sind nicht in Ordnung."));
        User user = userRepository.authenticate(request.account().trim(), request.password())
                .orElseThrow(() -> new LegacyXmlRequestException("101", "Die Authentifizierungsdaten sind nicht in Ordnung."));
        Courier courier = resolveCourier(request, user);
        if (courier == null || courier.getUserId() == null || !courier.getUserId().equals(user.getId())) {
            throw new LegacyXmlRequestException("101", "Die Authentifizierungsdaten sind nicht in Ordnung.");
        }
        if (customer.getHeadquartersId() != null
                && courier.getHeadquartersId() != null
                && !customer.getHeadquartersId().equals(courier.getHeadquartersId())) {
            throw new LegacyXmlRequestException("101", "Die Authentifizierungsdaten sind nicht in Ordnung.");
        }
        if (!hasValidSession(user.getId(), request.sessionId().trim())) {
            throw new LegacyXmlRequestException("101", "Die Authentifizierungsdaten sind nicht in Ordnung.");
        }

        CostCenter requestedCostCenter = null;
        if (!isBlank(request.costCenterName())) {
            requestedCostCenter = costCenterRepository.findByCustomerIdAndName(customer.getId(), request.costCenterName().trim())
                    .orElse(null);
        }
        CostCenter defaultCostCenter = resolveDefaultCostCenter(customer);

        return new AuthContext(customer, user, null, courier, requestedCostCenter, defaultCostCenter);
    }

    private Courier resolveCourier(AuthRequest request, User user) {
        if (!isBlank(request.authId())) {
            try {
                return courierRepository.findById(Objects.requireNonNull(Long.valueOf(request.authId().trim()))).orElse(null);
            } catch (NumberFormatException ignored) {
                return null;
            }
        }
        if (!isBlank(request.authEid())) {
            return courierRepository.findByEid(request.authEid().trim()).orElse(null);
        }
        return courierRepository.findByUserId(user.getId()).orElse(null);
    }

    private CostCenter resolveDefaultCostCenter(Customer customer) {
        if (customer == null || customer.getId() == null) {
            return null;
        }
        if (customer.getCostCenterPayerId() != null) {
            CostCenter payer = costCenterRepository.findById(Objects.requireNonNull(customer.getCostCenterPayerId())).orElse(null);
            if (payer != null) {
                return payer;
            }
        }
        CostCenter root = costCenterRepository.findFirstByCustomerIdAndParentIdIsNull(customer.getId()).orElse(null);
        if (root != null) {
            return root;
        }
        return costCenterRepository.findByCustomerId(customer.getId()).stream().findFirst().orElse(null);
    }

    private boolean hasValidSession(Long userId, String sessionId) {
        if (userId == null || isBlank(sessionId)) {
            return false;
        }
        String dbInstanceNo = parameterService.getParameterValue(ParameterKeys.GLOBAL_UNIQUE_DB_INSTANCE_NO, 0L);
        if (isBlank(dbInstanceNo)) {
            dbInstanceNo = parameterService.getParameterValueWithFallback(ParameterKeys.GLOBAL_UNIQUE_DB_INSTANCE_NO, 0L);
        }
        if (isBlank(dbInstanceNo)) {
            return false;
        }
        for (String sql : SESSION_QUERIES) {
            try {
                Integer count = jdbcTemplate.queryForObject(Objects.requireNonNull(sql), Integer.class, Objects.requireNonNull(dbInstanceNo), userId, sessionId);
                if (count != null && count > 0) {
                    return true;
                }
            } catch (Exception ex) {
                log.debug("Legacy XML B2B session query failed for SQL [{}]: {}", sql, ex.getMessage());
            }
        }
        return false;
    }

    private boolean isBlank(String value) {
        return value == null || value.isBlank();
    }

    public record AuthRequest(String authType,
                              String authId,
                              String authEid,
                              String customerEid,
                              String account,
                              String password,
                              String sessionId,
                              String costCenterName) {
    }

    public record AuthContext(Customer customer,
                              User user,
                              Employee employee,
                              Courier courier,
                              CostCenter requestedCostCenter,
                              CostCenter defaultCostCenter) {

        public CostCenter effectiveCostCenter() {
            return requestedCostCenter != null ? requestedCostCenter : defaultCostCenter;
        }
    }
}
