package com.magistr.app.config.tenant;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.PlatformTransactionManager;

import jakarta.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.io.File;
import java.io.IOException;
import java.util.*;

/**
 * Конфигурация мультитенантного DataSource.
 * Загружает тенанты из JSON-файла (mounted ConfigMap).
 *
 * Если нет ни одного настроенного тенанта — создаёт H2 in-memory БД
 * как заглушку, чтобы Spring JPA мог инициализироваться.
 */
@Configuration
public class TenantDataSourceConfig {

    private static final Logger log = LoggerFactory.getLogger(TenantDataSourceConfig.class);

    @Value("${app.tenants.config-path:tenants.json}")
    private String tenantsConfigPath;

    @Value("${spring.datasource.url:}")
    private String defaultDbUrl;

    @Value("${spring.datasource.username:}")
    private String defaultDbUsername;

    @Value("${spring.datasource.password:}")
    private String defaultDbPassword;

    @Bean
    @Primary
    public DataSource dataSource() {
        TenantRoutingDataSource routingDataSource = new TenantRoutingDataSource();

        // Загружаем тенантов из JSON (read-only ConfigMap mount)
        List<TenantConfig> tenants = loadTenantsFromFile();

        // Если нет тенантов и есть дефолтный datasource — создаём "default" тенант
        if (tenants.isEmpty() && defaultDbUrl != null && !defaultDbUrl.isBlank()) {
            TenantConfig defaultTenant = new TenantConfig(
                "Default", "default", defaultDbUrl, defaultDbUsername, defaultDbPassword
            );
            tenants.add(defaultTenant);
            log.info("No tenants config found, using default datasource: {}", defaultDbUrl);
        }

        // Регистрируем тенантов
        for (TenantConfig tenant : tenants) {
            try {
                routingDataSource.addTenant(tenant);
            } catch (Exception e) {
                log.error("Не удалось добавить тенанта '{}': {}", tenant.getDomain(), e.getMessage());
            }
        }

        // Если всё ещё нет ни одного тенанта — H2 in-memory заглушка
        if (routingDataSource.getTenantConfigs().isEmpty()) {
            log.warn("=== НЕТ НАСТРОЕННЫХ ТЕНАНТОВ ===");
            log.warn("Создаём H2 in-memory заглушку для запуска приложения.");
            log.warn("Добавьте тенант через POST /api/database/tenants");

            TenantConfig h2Fallback = new TenantConfig(
                "H2 Placeholder", "default",
                "jdbc:h2:mem:placeholder;DB_CLOSE_DELAY=-1",
                "sa", ""
            );
            routingDataSource.addTenant(h2Fallback);
        }

        return routingDataSource;
    }

    @Bean
    public TenantRoutingDataSource tenantRoutingDataSource(DataSource dataSource) {
        return (TenantRoutingDataSource) dataSource;
    }

    @Bean
    @Primary
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource);
        em.setPackagesToScan("com.magistr.app.model");

        HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
        vendorAdapter.setGenerateDdl(false);
        vendorAdapter.setDatabasePlatform("org.hibernate.dialect.PostgreSQLDialect");
        em.setJpaVendorAdapter(vendorAdapter);

        Map<String, Object> props = new HashMap<>();
        props.put("hibernate.hbm2ddl.auto", "none");
        props.put("hibernate.show_sql", "false");
        em.setJpaPropertyMap(props);

        return em;
    }

    @Bean
    @Primary
    public PlatformTransactionManager transactionManager(EntityManagerFactory emf) {
        return new JpaTransactionManager(emf);
    }

    private List<TenantConfig> loadTenantsFromFile() {
        File file = new File(tenantsConfigPath);
        if (!file.exists()) {
            log.info("Tenants config file not found: {}", tenantsConfigPath);
            return new ArrayList<>();
        }

        try {
            ObjectMapper mapper = new ObjectMapper();
            List<TenantConfig> list = mapper.readValue(file, new TypeReference<>() {});
            log.info("Loaded {} tenant(s) from {}", list.size(), tenantsConfigPath);
            return list;
        } catch (IOException e) {
            log.error("Не удалось прочитать конфиг тенантов: {}", e.getMessage());
            return new ArrayList<>();
        }
    }
}
