package com.writeoff.module.system.service;

import com.writeoff.common.api.PageResult;
import com.writeoff.module.export.dto.CreateExportTaskRequest;
import com.writeoff.module.export.model.ExportTaskInfo;
import com.writeoff.module.export.service.ExportTaskService;
import com.writeoff.module.system.model.OperationAuditLogInfo;
import com.writeoff.security.AuthContext;
import com.writeoff.security.AuthScope;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.List;

@Service
public class OperationAuditLogService {
    private static final Logger log = LoggerFactory.getLogger(OperationAuditLogService.class);
    private final JdbcTemplate jdbcTemplate;
    private final ExportTaskService exportTaskService;

    private static final RowMapper<OperationAuditLogInfo> ROW_MAPPER = (rs, n) -> new OperationAuditLogInfo(
            rs.getLong("id"),
            rs.getLong("tenant_id"),
            rs.getLong("user_id"),
            rs.getString("scope"),
            rs.getString("action_code"),
            rs.getString("biz_type"),
            rs.getString("biz_id"),
            rs.getString("http_method"),
            rs.getString("request_uri"),
            rs.getString("request_id"),
            rs.getInt("status_code"),
            rs.getInt("success") == 1,
            rs.getString("error_message"),
            rs.getString("ip"),
            rs.getString("created_at")
    );
    private static final RowMapper<ExportTaskInfo> EXPORT_TASK_ROW_MAPPER = (rs, n) -> new ExportTaskInfo(
            rs.getLong("id"),
            rs.getString("task_code"),
            rs.getString("biz_type"),
            rs.getString("biz_id"),
            rs.getString("file_name"),
            rs.getString("file_oss_key"),
            rs.getString("status"),
            rs.getInt("retry_count"),
            rs.getInt("download_count"),
            rs.getString("token_expire_at"),
            rs.getString("error_message"),
            rs.getString("created_at"),
            rs.getString("finished_at")
    );

    public OperationAuditLogService(JdbcTemplate jdbcTemplate, ExportTaskService exportTaskService) {
        this.jdbcTemplate = jdbcTemplate;
        this.exportTaskService = exportTaskService;
    }

    public void log(Long tenantId, Long userId, AuthScope scope, String actionCode, String bizType, String bizId,
                    String httpMethod, String requestUri, String requestQuery, String requestId, Integer statusCode,
                    boolean success, String errorMessage, String ip, String userAgent) {
        jdbcTemplate.update(
                "INSERT INTO operation_audit_log (tenant_id, user_id, scope, action_code, biz_type, biz_id, http_method, request_uri, request_query, request_id, status_code, success, error_message, ip, user_agent) " +
                        "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
                tenantId == null ? 0L : tenantId,
                userId == null ? 0L : userId,
                scope == null ? AuthScope.TENANT.name() : scope.name(),
                actionCode == null ? "UNKNOWN" : actionCode,
                bizType,
                bizId,
                httpMethod,
                requestUri,
                requestQuery,
                requestId,
                statusCode == null ? 200 : statusCode,
                success ? 1 : 0,
                errorMessage,
                ip,
                userAgent
        );
    }

    public PageResult<OperationAuditLogInfo> list(Long userId, String actionCode, Integer pageNo, Integer pageSize) {
        Long tenantId = tenantId();
        int resolvedPageNo = normalizePageNo(pageNo);
        int resolvedPageSize = normalizePageSize(pageSize);
        int offset = (resolvedPageNo - 1) * resolvedPageSize;
        StringBuilder whereSql = new StringBuilder(
                " FROM operation_audit_log WHERE UPPER(TRIM(IFNULL(scope, 'TENANT')))='TENANT' AND tenant_id=?"
        );
        List<Object> args = new ArrayList<Object>();
        args.add(tenantId);
        if (userId != null) {
            whereSql.append(" AND user_id=?");
            args.add(userId);
        }
        if (actionCode != null && !actionCode.trim().isEmpty()) {
            whereSql.append(" AND action_code=?");
            args.add(actionCode.trim());
        }
        String countSql = "SELECT COUNT(1)" + whereSql;
        Long total = jdbcTemplate.queryForObject(countSql, Long.class, args.toArray());
        String dataSql = "SELECT id, tenant_id, user_id, scope, action_code, biz_type, biz_id, http_method, request_uri, request_id, status_code, success, error_message, ip, " +
                "DATE_FORMAT(created_at, '%Y-%m-%d %H:%i:%s') AS created_at " +
                whereSql +
                " ORDER BY id DESC LIMIT ? OFFSET ?";
        List<Object> dataArgs = new ArrayList<Object>(args);
        dataArgs.add(resolvedPageSize);
        dataArgs.add(offset);
        log.info("[AuditLog][TENANT] SQL: {}", dataSql);
        log.info("[AuditLog][TENANT] ARGS: {}", args);
        List<OperationAuditLogInfo> list = jdbcTemplate.query(dataSql, ROW_MAPPER, dataArgs.toArray());
        return new PageResult<OperationAuditLogInfo>(list, total == null ? 0L : total, resolvedPageNo, resolvedPageSize);
    }

    public PageResult<OperationAuditLogInfo> listPlatform(Long userId, String actionCode, Long tenantId, String scope, Integer pageNo, Integer pageSize) {
        int resolvedPageNo = normalizePageNo(pageNo);
        int resolvedPageSize = normalizePageSize(pageSize);
        int offset = (resolvedPageNo - 1) * resolvedPageSize;
        StringBuilder whereSql = new StringBuilder(" FROM operation_audit_log WHERE 1=1");
        List<Object> args = new ArrayList<Object>();
        if (tenantId != null) {
            whereSql.append(" AND tenant_id=?");
            args.add(tenantId);
        }
        if (scope != null && !scope.trim().isEmpty()) {
            whereSql.append(" AND UPPER(TRIM(IFNULL(scope, ''))) = ?");
            args.add(scope.trim().toUpperCase());
        }
        if (userId != null) {
            whereSql.append(" AND user_id=?");
            args.add(userId);
        }
        if (actionCode != null && !actionCode.trim().isEmpty()) {
            whereSql.append(" AND action_code=?");
            args.add(actionCode.trim());
        }
        String countSql = "SELECT COUNT(1)" + whereSql;
        Long total = jdbcTemplate.queryForObject(countSql, Long.class, args.toArray());
        String dataSql = "SELECT id, tenant_id, user_id, scope, action_code, biz_type, biz_id, http_method, request_uri, request_id, status_code, success, error_message, ip, " +
                "DATE_FORMAT(created_at, '%Y-%m-%d %H:%i:%s') AS created_at " +
                whereSql +
                " ORDER BY id DESC LIMIT ? OFFSET ?";
        List<Object> dataArgs = new ArrayList<Object>(args);
        dataArgs.add(resolvedPageSize);
        dataArgs.add(offset);
        log.info("[AuditLog][PLATFORM] SQL: {}", dataSql);
        log.info("[AuditLog][PLATFORM] ARGS: {}", args);
        List<OperationAuditLogInfo> list = jdbcTemplate.query(dataSql, ROW_MAPPER, dataArgs.toArray());
        return new PageResult<OperationAuditLogInfo>(list, total == null ? 0L : total, resolvedPageNo, resolvedPageSize);
    }

    public PageResult<ExportTaskInfo> listExportTasks() {
        List<ExportTaskInfo> list = jdbcTemplate.query(
                "SELECT id, task_code, biz_type, biz_id, file_name, file_oss_key, status, retry_count, IFNULL(download_count,0) AS download_count, " +
                        "DATE_FORMAT(download_token_expire_at, '%Y-%m-%d %H:%i:%s') AS token_expire_at, error_message, " +
                        "DATE_FORMAT(created_at, '%Y-%m-%d %H:%i:%s') AS created_at, DATE_FORMAT(finished_at, '%Y-%m-%d %H:%i:%s') AS finished_at " +
                        "FROM export_task WHERE tenant_id=? AND biz_type='AUDIT_LOG' AND is_deleted=0 ORDER BY id DESC LIMIT 300",
                EXPORT_TASK_ROW_MAPPER,
                tenantId()
        );
        return new PageResult<ExportTaskInfo>(list, list.size(), 1, 300);
    }

    public java.util.Map<String, Object> createExportTask(Long userId, String actionCode, String idempotencyKey, String fileName) {
        CreateExportTaskRequest request = new CreateExportTaskRequest();
        request.setIdempotencyKey(idempotencyKey);
        request.setTaskCode("AUDIT_LOG_EXPORT");
        request.setBizType("AUDIT_LOG");
        request.setBizId(null);
        request.setFiltersJson(buildFilterJson(userId, actionCode));
        request.setFileName(fileName);
        return exportTaskService.create(request);
    }

    private String buildFilterJson(Long userId, String actionCode) {
        StringBuilder sb = new StringBuilder("{");
        boolean hasPrev = false;
        if (userId != null) {
            sb.append("\"userId\":").append(userId);
            hasPrev = true;
        }
        if (actionCode != null && !actionCode.trim().isEmpty()) {
            if (hasPrev) {
                sb.append(",");
            }
            sb.append("\"actionCode\":\"").append(actionCode.trim().replace("\"", "")).append("\"");
        }
        sb.append("}");
        return sb.toString();
    }

    private Long tenantId() {
        return AuthContext.requireTenantId();
    }

    private int normalizePageNo(Integer pageNo) {
        if (pageNo == null || pageNo < 1) {
            return 1;
        }
        return pageNo;
    }

    private int normalizePageSize(Integer pageSize) {
        if (pageSize == null || pageSize < 1) {
            return 20;
        }
        return Math.min(pageSize, 200);
    }
}
