package com.magistr.app.controller;

import com.magistr.app.config.auth.RequireRoles;
import com.magistr.app.config.tenant.TenantConfig;
import com.magistr.app.config.tenant.TenantConfigWatcher;
import com.magistr.app.config.tenant.TenantContext;
import com.magistr.app.config.tenant.TenantRoutingDataSource;
import com.magistr.app.model.Role;
import com.magistr.app.service.TenantLifecycleException;
import com.magistr.app.service.TenantLifecycleMutationResult;
import com.magistr.app.service.TenantLifecycleService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;

/**
 * API управления подключениями к базам данных (тенантами).
 * Доступно только для ADMIN.
 *
 * Изменения проходят безопасный lifecycle: подготовка и миграция временного
 * пула, сохранение desired-конфигурации и только затем локальная активация.
 */
@RestController
@RequestMapping("/api/database")
@RequireRoles({Role.ADMIN})
public class DatabaseController {

    private final TenantRoutingDataSource routingDataSource;
    private final TenantLifecycleService tenantLifecycleService;
    private final TenantConfigWatcher tenantConfigWatcher;

    public DatabaseController(TenantRoutingDataSource routingDataSource,
                              TenantLifecycleService tenantLifecycleService,
                              TenantConfigWatcher tenantConfigWatcher) {
        this.routingDataSource = routingDataSource;
        this.tenantLifecycleService = tenantLifecycleService;
        this.tenantConfigWatcher = tenantConfigWatcher;
    }

    /**
     * Статус текущего подключения (по домену запроса).
     */
    @GetMapping("/status")
    public ResponseEntity<Map<String, Object>> getStatus() {
        String currentTenant = TenantContext.getCurrentTenant();
        boolean connected = routingDataSource.testConnection(currentTenant);

        TenantConfig config = routingDataSource.getTenantConfigs().get(currentTenant);

        Map<String, Object> result = new HashMap<>();
        result.put("tenant", currentTenant);
        result.put("connected", connected);
        result.put("configured", config != null);
        if (config != null) {
            result.put("name", config.getName());
            result.put("url", config.getUrl());
        }
        return ResponseEntity.ok(result);
    }

    /**
     * Список всех тенантов.
     */
    @GetMapping("/tenants")
    public ResponseEntity<List<Map<String, Object>>> getTenants() {
        List<Map<String, Object>> result = new ArrayList<>();

        for (TenantConfig config : routingDataSource.getTenantConfigs().values()) {
            Map<String, Object> tenant = new HashMap<>();
            tenant.put("name", config.getName());
            tenant.put("domain", config.getDomain());
            tenant.put("url", config.getUrl());
            tenant.put("username", config.getUsername());
            tenant.put("connected", routingDataSource.testConnection(config.getDomain()));
            result.add(tenant);
        }

        return ResponseEntity.ok(result);
    }

    /**
     * Добавить новый тенант.
     */
    @PostMapping("/tenants")
    public ResponseEntity<Map<String, Object>> addTenant(@RequestBody TenantConfig config) {
        try {
            return tenantLifecycleService.executeSerialized(() -> {
                tenantConfigWatcher.prepareForApiMutation();
                TenantLifecycleMutationResult mutation =
                        tenantLifecycleService.addOrUpdateTenantWithReceipt(config);
                tenantConfigWatcher.registerSuccessfulMutation(mutation.receipt());
                TenantConfig saved = mutation.tenant();
                Map<String, Object> result = new HashMap<>();
                result.put("success", true);
                result.put("message", "Тенант '" + saved.getDomain() + "' добавлен");
                return ResponseEntity.ok(result);
            });
        } catch (IllegalArgumentException validationFailure) {
            return lifecycleError(HttpStatus.BAD_REQUEST, validationFailure.getMessage());
        } catch (TenantLifecycleException lifecycleFailure) {
            return lifecycleError(HttpStatus.SERVICE_UNAVAILABLE, lifecycleFailure.getMessage());
        }
    }

    /**
     * Удалить тенант.
     */
    @DeleteMapping("/tenants/{domain}")
    public ResponseEntity<Map<String, Object>> removeTenant(@PathVariable("domain") String domain) {
        try {
            return tenantLifecycleService.executeSerialized(() -> {
                tenantConfigWatcher.prepareForApiMutation();
                TenantLifecycleMutationResult mutation =
                        tenantLifecycleService.removeTenantWithReceipt(domain);
                tenantConfigWatcher.registerSuccessfulMutation(mutation.receipt());
                TenantConfig removed = mutation.tenant();
                Map<String, Object> result = new HashMap<>();
                result.put("success", true);
                result.put("message", "Тенант '" + removed.getDomain() + "' удалён");
                return ResponseEntity.ok(result);
            });
        } catch (NoSuchElementException notFound) {
            return lifecycleError(HttpStatus.NOT_FOUND, notFound.getMessage());
        } catch (IllegalArgumentException validationFailure) {
            return lifecycleError(HttpStatus.BAD_REQUEST, validationFailure.getMessage());
        } catch (TenantLifecycleException lifecycleFailure) {
            return lifecycleError(HttpStatus.SERVICE_UNAVAILABLE, lifecycleFailure.getMessage());
        }
    }

    /**
     * Тест подключения к произвольной БД.
     */
    @PostMapping("/test")
    public ResponseEntity<Map<String, Object>> testConnection(@RequestBody Map<String, String> params) {
        String url = params.get("url");
        String username = params.get("username");
        String password = params.get("password");

        Map<String, Object> result = new HashMap<>();

        if (url == null || url.isBlank()) {
            result.put("success", false);
            result.put("message", "URL не указан");
            return ResponseEntity.badRequest().body(result);
        }

        String testResult = routingDataSource.testExternalConnection(url, username, password);
        boolean success = "OK".equals(testResult);

        result.put("success", success);
        result.put("message", success ? "Подключение успешно!" : testResult);
        return ResponseEntity.ok(result);
    }

    private ResponseEntity<Map<String, Object>> lifecycleError(HttpStatus status, String message) {
        Map<String, Object> result = new HashMap<>();
        result.put("success", false);
        result.put("message", message);
        return ResponseEntity.status(status).body(result);
    }
}
