package com.eactive.eai.rms.common.dbconsole.controller;

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

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.eactive.eai.rms.common.interceptor.InterceptorSkipController;
import com.eactive.eai.rms.common.login.LoginVo;
import com.eactive.eai.rms.common.login.SessionManager;
import com.eactive.eai.rms.common.dbconsole.dto.ColumnInfoDto;
import com.eactive.eai.rms.common.dbconsole.dto.DatasourceInfoDto;
import com.eactive.eai.rms.common.dbconsole.dto.SqlRequestDto;
import com.eactive.eai.rms.common.dbconsole.dto.SqlResultDto;
import com.eactive.eai.rms.common.dbconsole.dto.TableInfoDto;
import com.eactive.eai.rms.common.dbconsole.service.DbConsoleDecryptService;
import com.eactive.eai.rms.common.dbconsole.service.DbConsoleService;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * DB Console Controller
 * - admin 그룹 사용자만 접근 가능
 */
@Controller
@RequestMapping("/admin/dbconsole")
public class DbConsoleController implements InterceptorSkipController {

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

    private final DbConsoleService dbConsoleService;
    private final DbConsoleDecryptService decryptService;
    private final ObjectMapper objectMapper;

    public DbConsoleController(DbConsoleService dbConsoleService, DbConsoleDecryptService decryptService) {
        this.dbConsoleService = dbConsoleService;
        this.decryptService = decryptService;
        this.objectMapper = new ObjectMapper();
    }

    /**
     * Admin Role 권한 체크
     */
    private void checkAdminAccess(HttpServletRequest request) {
        LoginVo loginVo = SessionManager.getLoginVo(request);
        if (loginVo == null) {
            throw new AccessDeniedException("로그인이 필요합니다.");
        }

        List<String> roleList = loginVo.getRoleList();
        if (roleList == null || roleList.isEmpty()) {
            log.warn("DB Console 접근 거부 (역할 없음): userId={}", loginVo.getUserId());
            throw new AccessDeniedException("DB Console 접근 권한이 없습니다.");
        }

        // admin 역할 체크
        boolean hasAdminRole = roleList.stream()
            .anyMatch(role -> role != null &&
                     ("ADMIN".equalsIgnoreCase(role) || role.toUpperCase().contains("ADMIN")));

        if (!hasAdminRole) {
            log.warn("DB Console 접근 거부 (Admin 권한 없음): userId={}, roles={}",
                    loginVo.getUserId(), roleList);
            throw new AccessDeniedException("DB Console 접근 권한이 없습니다. (Admin 권한 필요)");
        }
    }

    /**
     * DB Console 페이지
     */
    @GetMapping("/console.do")
    public String dbConsolePage(HttpServletRequest request, Model model) {
        checkAdminAccess(request);

        try {
            List<DatasourceInfoDto> datasources = dbConsoleService.getAvailableDatasources();
            String sessionId = dbConsoleService.createSession();

            model.addAttribute("datasources", datasources);
            model.addAttribute("datasourcesJson", objectMapper.writeValueAsString(datasources));
            model.addAttribute("sessionId", sessionId);
        } catch (Exception e) {
            log.error("DB Console 페이지 로드 오류", e);
            model.addAttribute("datasourcesJson", "[]");
            model.addAttribute("sessionId", "");
        }

        return "/common/dbconsole/dbConsoleMan";
    }

    /**
     * 사용 가능한 데이터소스 목록 조회
     */
    @PostMapping("/datasources.do")
    @ResponseBody
    public ResponseEntity<List<DatasourceInfoDto>> getDatasources(HttpServletRequest request) {
        checkAdminAccess(request);
        return ResponseEntity.ok(dbConsoleService.getAvailableDatasources());
    }

    /**
     * 새 세션 생성
     */
    @PostMapping("/session.do")
    @ResponseBody
    public ResponseEntity<Map<String, String>> createSession(HttpServletRequest request) {
        checkAdminAccess(request);
        String sessionId = dbConsoleService.createSession();
        Map<String, String> result = new HashMap<>();
        result.put("sessionId", sessionId);
        return ResponseEntity.ok(result);
    }

    /**
     * SQL 실행
     */
    @PostMapping("/execute.do")
    @ResponseBody
    public ResponseEntity<SqlResultDto> executeSql(HttpServletRequest request, @RequestBody SqlRequestDto sqlRequest) {
        checkAdminAccess(request);
        log.debug("SQL 실행 요청: session={}, action={}", sqlRequest.getSessionId(), sqlRequest.getAction());
        SqlResultDto result = dbConsoleService.executeSql(sqlRequest);
        return ResponseEntity.ok(result);
    }

    /**
     * COMMIT
     */
    @PostMapping("/commit.do")
    @ResponseBody
    public ResponseEntity<SqlResultDto> commit(HttpServletRequest request, @RequestBody SqlRequestDto sqlRequest) {
        checkAdminAccess(request);
        sqlRequest.setAction("COMMIT");
        SqlResultDto result = dbConsoleService.executeSql(sqlRequest);
        return ResponseEntity.ok(result);
    }

    /**
     * ROLLBACK
     */
    @PostMapping("/rollback.do")
    @ResponseBody
    public ResponseEntity<SqlResultDto> rollback(HttpServletRequest request, @RequestBody SqlRequestDto sqlRequest) {
        checkAdminAccess(request);
        sqlRequest.setAction("ROLLBACK");
        SqlResultDto result = dbConsoleService.executeSql(sqlRequest);
        return ResponseEntity.ok(result);
    }

    /**
     * 세션 종료
     */
    @PostMapping("/disconnect.do")
    @ResponseBody
    public ResponseEntity<SqlResultDto> disconnect(HttpServletRequest request, @RequestBody SqlRequestDto sqlRequest) {
        checkAdminAccess(request);
        sqlRequest.setAction("DISCONNECT");
        SqlResultDto result = dbConsoleService.executeSql(sqlRequest);
        return ResponseEntity.ok(result);
    }

    /**
     * 스키마 목록 조회
     */
    @PostMapping("/schemas.do")
    @ResponseBody
    public ResponseEntity<List<String>> getSchemas(HttpServletRequest request, @RequestBody SqlRequestDto sqlRequest) {
        checkAdminAccess(request);
        List<String> schemas = dbConsoleService.getSchemas(sqlRequest);
        return ResponseEntity.ok(schemas);
    }

    /**
     * 테이블 목록 조회
     */
    @PostMapping("/tables.do")
    @ResponseBody
    public ResponseEntity<List<TableInfoDto>> getTables(HttpServletRequest request, @RequestBody SqlRequestDto sqlRequest) {
        checkAdminAccess(request);
        List<TableInfoDto> tables = dbConsoleService.getTables(sqlRequest);
        return ResponseEntity.ok(tables);
    }

    /**
     * 테이블 컬럼 정보 조회
     */
    @PostMapping("/columns.do")
    @ResponseBody
    public ResponseEntity<List<ColumnInfoDto>> getColumns(HttpServletRequest request, @RequestBody SqlRequestDto sqlRequest) {
        checkAdminAccess(request);
        List<ColumnInfoDto> columns = dbConsoleService.getColumns(sqlRequest);
        return ResponseEntity.ok(columns);
    }

    /**
     * 테이블 DDL 조회
     */
    @PostMapping("/ddl.do")
    @ResponseBody
    public ResponseEntity<Map<String, String>> getTableDdl(HttpServletRequest request, @RequestBody SqlRequestDto sqlRequest) {
        checkAdminAccess(request);
        String ddl = dbConsoleService.getTableDdl(sqlRequest);
        Map<String, String> result = new HashMap<>();
        result.put("ddl", ddl);
        return ResponseEntity.ok(result);
    }

    /**
     * 세션 하트비트 (커넥션 유지용)
     */
    @PostMapping("/heartbeat.do")
    @ResponseBody
    public ResponseEntity<SqlResultDto> heartbeat(HttpServletRequest request, @RequestBody SqlRequestDto sqlRequest) {
        checkAdminAccess(request);
        SqlResultDto result = dbConsoleService.heartbeat(sqlRequest.getSessionId());
        // 남은 시간도 함께 반환
        long remainingSeconds = dbConsoleService.getSessionRemainingTime(sqlRequest.getSessionId());
        result.setMessage(result.getMessage() + " (남은시간: " + remainingSeconds + "초)");
        return ResponseEntity.ok(result);
    }

    /**
     * SafeDB 암호화 데이터 복호화 (단일 값)
     */
    @PostMapping("/decrypt.do")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> decryptValue(HttpServletRequest request, @RequestBody Map<String, String> params) {
        checkAdminAccess(request);

        String encryptedValue = params.get("value");
        if (encryptedValue == null || encryptedValue.isEmpty()) {
            Map<String, Object> result = new HashMap<>();
            result.put("success", false);
            result.put("message", "복호화할 값이 없습니다.");
            return ResponseEntity.ok(result);
        }

        try {
            String decryptedValue = decryptService.decryptValue(encryptedValue);
            Map<String, Object> result = new HashMap<>();
            result.put("success", true);
            result.put("originalValue", encryptedValue);
            result.put("decryptedValue", decryptedValue);
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            log.error("복호화 오류", e);
            Map<String, Object> result = new HashMap<>();
            result.put("success", false);
            result.put("message", "복호화 오류: " + e.getMessage());
            return ResponseEntity.ok(result);
        }
    }

    /**
     * SafeDB 암호화 데이터 복호화 (여러 값)
     */
    @PostMapping("/decryptBatch.do")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> decryptBatch(HttpServletRequest request, @RequestBody Map<String, List<String>> params) {
        checkAdminAccess(request);

        List<String> encryptedValues = params.get("values");
        if (encryptedValues == null || encryptedValues.isEmpty()) {
            Map<String, Object> result = new HashMap<>();
            result.put("success", false);
            result.put("message", "복호화할 값이 없습니다.");
            return ResponseEntity.ok(result);
        }

        try {
            Map<String, String> decryptedMap = decryptService.decryptValues(encryptedValues);
            Map<String, Object> result = new HashMap<>();
            result.put("success", true);
            result.put("decrypted", decryptedMap);
            return ResponseEntity.ok(result);
        } catch (Exception e) {
            log.error("복호화 오류", e);
            Map<String, Object> result = new HashMap<>();
            result.put("success", false);
            result.put("message", "복호화 오류: " + e.getMessage());
            return ResponseEntity.ok(result);
        }
    }

    /**
     * 값이 암호화되었는지 확인
     */
    @PostMapping("/checkEncrypted.do")
    @ResponseBody
    public ResponseEntity<Map<String, Object>> checkEncrypted(HttpServletRequest request, @RequestBody Map<String, String> params) {
        checkAdminAccess(request);

        String value = params.get("value");
        boolean isEncrypted = decryptService.isEncrypted(value);

        Map<String, Object> result = new HashMap<>();
        result.put("success", true);
        result.put("value", value);
        result.put("isEncrypted", isEncrypted);
        return ResponseEntity.ok(result);
    }
}
