import { NextResponse } from "next/server";
import { validateApiKey } from "./auth.js";
import { mutateWithRLS, queryWithRLS } from "../db.js";
import { checkRateLimit, addRateLimitHeaders } from "./rate-limit.js";
import { getApplicationConfig } from "../config/index.js";
import { auth } from "../auth.js";
import { MetaService } from "../services/meta.service.js";
import { ScopeService } from "../services/scope.service.js";
import { getChildEntities } from "../entities/queries.js";
import { getCorsOrigins, normalizeOrigin } from "../utils/cors.js";
async function validateAndAuthenticateRequest(request) {
  var _a;
  const cookieHeader = request.headers.get("cookie");
  if (cookieHeader) {
    try {
      const session = await auth.api.getSession({
        headers: new Headers({
          cookie: cookieHeader
        })
      });
      if ((_a = session == null ? void 0 : session.user) == null ? void 0 : _a.id) {
        const userRole = session.user.role || "member";
        const userFlags = session.user.flags || [];
        const sessionAuth = {
          userId: session.user.id,
          scopes: generateScopesForRole(userRole, userFlags),
          flags: userFlags,
          isSession: true
        };
        console.log(`[Auth] Session authentication successful for user ${session.user.id} with role ${userRole}`);
        return { auth: sessionAuth };
      }
    } catch (sessionError) {
      console.log(`[Auth] Session authentication failed:`, sessionError);
    }
  }
  console.log(`[Auth] Falling back to API key authentication`);
  return validateAndAuthenticateApiRequest(request);
}
function generateScopesForRole(role, flags = []) {
  const baseScopes = getBaseScopesFromRegistry();
  let scopes = [...baseScopes];
  const roleScopes = getRoleScopesFromRegistry(role);
  scopes = [...scopes, ...roleScopes];
  for (const flag of flags) {
    const flagScopes = getFlagScopesFromRegistry(flag);
    scopes = [...scopes, ...flagScopes];
  }
  scopes = applyRestrictionRulesFromRegistry(scopes, flags);
  return scopes;
}
function getBaseScopesFromRegistry() {
  return ScopeService.getBaseScopes();
}
function getRoleScopesFromRegistry(role) {
  return ScopeService.getRoleScopes(role);
}
function getFlagScopesFromRegistry(flag) {
  return ScopeService.getFlagScopes(flag);
}
function applyRestrictionRulesFromRegistry(scopes, flags) {
  let filteredScopes = [...scopes];
  for (const flag of flags) {
    const rules = ScopeService.getRestrictionRules(flag);
    if (rules.remove && rules.remove.length > 0) {
      filteredScopes = filteredScopes.filter(
        (scope) => !rules.remove.some((pattern) => scope.includes(pattern))
      );
    }
    if (rules.allow_only && rules.allow_only.length > 0) {
      filteredScopes = filteredScopes.filter(
        (scope) => rules.allow_only.some((allowed) => scope.includes(allowed) || scope === allowed)
      );
    }
  }
  return filteredScopes;
}
async function validateAndAuthenticateApiRequest(request) {
  const auth2 = await validateApiKey(request);
  if (!auth2) {
    throw new Error("Invalid API key");
  }
  const rateLimitResult = checkRateLimit(auth2.keyId);
  if (!rateLimitResult.allowed) {
    const response = NextResponse.json(
      {
        success: false,
        error: "Rate limit exceeded",
        message: `Too many requests. Limit: ${rateLimitResult.limit} requests per minute`,
        code: "RATE_LIMIT_EXCEEDED",
        retryAfter: Math.ceil((rateLimitResult.resetTime - Date.now()) / 1e3)
      },
      { status: 429 }
    );
    addRateLimitHeaders(response, auth2.keyId, auth2.scopes);
    return { auth: auth2, rateLimitResponse: response };
  }
  return { auth: auth2 };
}
function getApiAuth(request) {
  const userId = request.headers.get("x-api-user-id");
  const keyId = request.headers.get("x-api-key-id");
  const scopesHeader = request.headers.get("x-api-scopes");
  if (!userId || !keyId || !scopesHeader) {
    throw new Error("Missing API authentication headers");
  }
  let scopes = [];
  try {
    const parsed = JSON.parse(scopesHeader);
    if (Array.isArray(parsed)) {
      scopes = parsed.filter((s) => typeof s === "string");
    }
  } catch {
    console.warn("[API] Invalid scopes header format");
  }
  return {
    userId,
    keyId,
    scopes
  };
}
function checkScope(auth2, requiredScope) {
  const hasRequiredScope = auth2.scopes.includes(requiredScope);
  if (!hasRequiredScope) {
    return NextResponse.json(
      {
        success: false,
        error: "Insufficient permissions",
        message: `This operation requires the '${requiredScope}' scope`,
        required_scope: requiredScope,
        your_scopes: auth2.scopes,
        code: "INSUFFICIENT_PERMISSIONS"
      },
      { status: 403 }
    );
  }
  return null;
}
function createApiResponse(data, responseMeta, status = 200) {
  return NextResponse.json({
    success: true,
    data,
    info: {
      timestamp: (/* @__PURE__ */ new Date()).toISOString(),
      ...responseMeta
    }
  }, { status });
}
function createApiError(message, status = 400, details, code) {
  return NextResponse.json({
    success: false,
    error: message,
    code: code || `HTTP_${status}`,
    details,
    info: {
      timestamp: (/* @__PURE__ */ new Date()).toISOString()
    }
  }, { status });
}
function parsePaginationParams(request) {
  const { searchParams } = new URL(request.url);
  const page = Math.max(1, parseInt(searchParams.get("page") || "1"));
  const limit = Math.min(100, Math.max(1, parseInt(searchParams.get("limit") || "10")));
  const offset = (page - 1) * limit;
  return { page, limit, offset };
}
function createPaginationMeta(page, limit, total) {
  return {
    page,
    limit,
    total,
    totalPages: Math.ceil(total / limit),
    hasNextPage: page * limit < total,
    hasPrevPage: page > 1
  };
}
async function logApiUsage(auth2, request, statusCode, responseTime, requestBody) {
  try {
    const endpoint = request.nextUrl.pathname;
    const method = request.method;
    const ipAddress = request.headers.get("x-forwarded-for") || request.headers.get("x-real-ip") || "unknown";
    const userAgent = request.headers.get("user-agent");
    await mutateWithRLS(
      `INSERT INTO "api_audit_log" 
       ("apiKeyId", "userId", endpoint, method, "statusCode", "ipAddress", "userAgent", "requestBody", "responseTime") 
       VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
      [
        auth2.keyId,
        auth2.userId,
        endpoint,
        method,
        statusCode,
        ipAddress,
        userAgent,
        requestBody ? JSON.stringify(requestBody) : null,
        responseTime || null
      ],
      auth2.userId
    );
  } catch (error) {
    console.error("Failed to log API usage:", error);
  }
}
function withApiLogging(handler) {
  return async (request, ...args) => {
    const startTime = Date.now();
    let auth2 = null;
    let requestBody = null;
    try {
      try {
        auth2 = getApiAuth(request);
      } catch {
      }
      if (["POST", "PUT", "PATCH"].includes(request.method)) {
        try {
          const clonedRequest = request.clone();
          requestBody = await clonedRequest.json();
        } catch {
        }
      }
      const response = await handler(request, ...args);
      const responseTime = Date.now() - startTime;
      if (auth2) {
        logApiUsage(auth2, request, response.status, responseTime, requestBody).catch(console.error);
      }
      return response;
    } catch (error) {
      const responseTime = Date.now() - startTime;
      if (auth2) {
        logApiUsage(auth2, request, 500, responseTime, requestBody).catch(console.error);
      }
      throw error;
    }
  };
}
function isValidUUID(uuid) {
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
  return uuidRegex.test(uuid);
}
function sanitizeSearchQuery(query) {
  if (!query || typeof query !== "string") {
    return "";
  }
  return query.replace(/[<>'"%;()&+]/g, "").trim().substring(0, 100);
}
function parseFilters(request) {
  const { searchParams } = new URL(request.url);
  const filters = {};
  const allowedFilters = getAllowedFiltersFromRegistry();
  for (const filter of allowedFilters) {
    const value = searchParams.get(filter);
    if (value) {
      filters[filter] = sanitizeSearchQuery(value);
    }
  }
  return filters;
}
function getAllowedFiltersFromRegistry() {
  return ScopeService.getAllowedFilters();
}
async function addCorsHeaders(response, request) {
  const env = process.env.NODE_ENV || "development";
  const config = await getApplicationConfig();
  const corsConfig = config.api.cors;
  let allowedOrigin = "null";
  if (request) {
    const origin = request.headers.get("origin");
    if (origin) {
      const normalizedOrigin = normalizeOrigin(origin);
      if (env === "development" && corsConfig.allowAllOrigins.development) {
        allowedOrigin = normalizedOrigin;
      } else {
        const allowedOrigins = getCorsOrigins(config, env);
        if (allowedOrigins.includes(normalizedOrigin)) {
          allowedOrigin = normalizedOrigin;
        }
      }
    }
  } else if (env === "development" && corsConfig.allowAllOrigins.development) {
    const allowedOrigins = getCorsOrigins(config, env);
    allowedOrigin = allowedOrigins[0] || "http://localhost:3000";
  }
  response.headers.set("Access-Control-Allow-Origin", allowedOrigin);
  response.headers.set("Access-Control-Allow-Methods", "GET, POST, PUT, PATCH, DELETE, OPTIONS");
  response.headers.set("Access-Control-Allow-Headers", "Content-Type, Authorization, X-API-Key, x-team-id, x-builder-source");
  response.headers.set("Access-Control-Allow-Credentials", "true");
  response.headers.set("Access-Control-Max-Age", "86400");
  return response;
}
async function handleCorsPreflightRequest(request) {
  const response = new NextResponse(null, { status: 200 });
  return await addCorsHeaders(response, request);
}
async function wrapAuthHandlerWithCors(handler, request) {
  const response = await handler();
  const newResponse = new Response(response.body, {
    status: response.status,
    statusText: response.statusText,
    headers: new Headers(response.headers)
  });
  const env = process.env.NODE_ENV || "development";
  const config = await getApplicationConfig();
  const corsConfig = config.api.cors;
  const origin = request.headers.get("origin");
  if (origin) {
    const normalizedOrigin = normalizeOrigin(origin);
    let allowedOrigin = "null";
    if (env === "development" && corsConfig.allowAllOrigins.development) {
      allowedOrigin = normalizedOrigin;
    } else {
      const allowedOrigins = getCorsOrigins(config, env);
      if (allowedOrigins.includes(normalizedOrigin)) {
        allowedOrigin = normalizedOrigin;
      }
    }
    newResponse.headers.set("Access-Control-Allow-Origin", allowedOrigin);
    newResponse.headers.set("Access-Control-Allow-Credentials", "true");
  }
  return newResponse;
}
function parseMetaParams(request) {
  const url = new URL(request.url);
  const metasParam = url.searchParams.get("metas");
  if (!metasParam) {
    return {
      includeMetadata: false,
      includeAll: false
    };
  }
  if (metasParam === "all") {
    return {
      includeMetadata: true,
      includeAll: true
    };
  }
  const specificKeys = metasParam.split(",").map((key) => key.trim()).filter((key) => key.length > 0);
  return {
    includeMetadata: true,
    includeAll: false,
    specificKeys
  };
}
function parseChildParams(request) {
  const url = new URL(request.url);
  const childParam = url.searchParams.get("child");
  if (!childParam) {
    return {
      includeChildren: false,
      includeAll: false
    };
  }
  if (childParam === "all") {
    return {
      includeChildren: true,
      includeAll: true
    };
  }
  const specificChildren = childParam.split(",").map((child) => child.trim()).filter((child) => child.length > 0);
  return {
    includeChildren: true,
    includeAll: false,
    specificChildren
  };
}
async function includeEntityMetadata(entityType, entities, metaParams, userId) {
  if (!metaParams.includeMetadata || entities.length === 0) {
    return entities;
  }
  try {
    const entityIds = entities.map((entity) => entity.id);
    let bulkMetadata;
    if (metaParams.includeAll) {
      bulkMetadata = await MetaService.getBulkEntityMetas(entityType, entityIds, userId, true);
    } else if (metaParams.specificKeys && metaParams.specificKeys.length > 0) {
      bulkMetadata = await MetaService.getBulkSpecificEntityMetas(entityType, entityIds, metaParams.specificKeys, userId);
    } else {
      bulkMetadata = {};
    }
    return entities.map((entity) => ({
      ...entity,
      metas: bulkMetadata[entity.id] || {}
    }));
  } catch (error) {
    console.error(`Error fetching bulk ${entityType} metadata:`, error);
    return entities;
  }
}
async function includeEntityChildren(entityName, entities, childParams, userId, entityConfig) {
  if (!childParams.includeChildren || entities.length === 0) {
    return entities;
  }
  try {
    const childEntities = getChildEntities(entityName);
    if (!childEntities || childEntities.length === 0) {
      return entities;
    }
    const entityIds = entities.map((entity) => entity.id);
    let childTypesToLoad;
    if (childParams.includeAll) {
      childTypesToLoad = childEntities.map((child) => child.name);
    } else if (childParams.specificChildren) {
      childTypesToLoad = childParams.specificChildren.filter(
        (childType) => childEntities.some((child) => child.name === childType)
      );
    } else {
      childTypesToLoad = [];
    }
    const childData = {};
    for (const entityId of entityIds) {
      childData[entityId] = {};
      for (const childType of childTypesToLoad) {
        const childEntity = childEntities.find((child) => child.name === childType);
        if (!childEntity) continue;
        const parentIdColumn = "parentId";
        const query = `
          SELECT * FROM "${childEntity.tableName}"
          WHERE "${parentIdColumn}" = $1
          ORDER BY "createdAt" DESC
        `;
        try {
          const childRows = await queryWithRLS(query, [entityId], userId);
          childData[entityId][childType] = childRows || [];
        } catch (error) {
          console.error(`Error loading ${childType} for ${entityName} ${entityId}:`, error);
          childData[entityId][childType] = [];
        }
      }
    }
    return entities.map((entity) => ({
      ...entity,
      child: childData[entity.id] || {}
    }));
  } catch (error) {
    console.error(`Error fetching child entities for ${entityName}:`, error);
    return entities;
  }
}
async function includeEntityMetadataForSingle(entityType, entity, metaParams, userId) {
  if (!metaParams.includeMetadata) {
    return entity;
  }
  try {
    let metadata;
    if (metaParams.includeAll) {
      metadata = await MetaService.getEntityMetas(entityType, entity.id, userId, true);
    } else if (metaParams.specificKeys && metaParams.specificKeys.length > 0) {
      metadata = await MetaService.getSpecificEntityMetas(entityType, entity.id, metaParams.specificKeys, userId);
    } else {
      metadata = {};
    }
    return {
      ...entity,
      metas: metadata
    };
  } catch (error) {
    console.error(`Error fetching ${entityType} metadata:`, error);
    return entity;
  }
}
async function handleEntityMetadataInResponse(entityType, entity, metaWasProvided, userId) {
  if (!metaWasProvided) {
    return entity;
  }
  try {
    const allEntityMetadata = await MetaService.getEntityMetas(entityType, entity.id, userId, true);
    return {
      ...entity,
      metas: allEntityMetadata
    };
  } catch (error) {
    console.error(`Error fetching ${entityType} metadata for response:`, error);
    return entity;
  }
}
function detectMetaDataType(value) {
  if (typeof value === "number" || typeof value === "string" && !isNaN(Number(value)) && value.trim() !== "") {
    return "number";
  }
  if (typeof value === "boolean") {
    return "boolean";
  }
  if (typeof value === "string") {
    return "string";
  }
  return "json";
}
async function processEntityMetadata(entityType, entityId, meta, userId) {
  const errors = [];
  let success = true;
  if (meta && typeof meta === "object" && Object.keys(meta).length > 0) {
    try {
      const existingMetadata = await MetaService.getEntityMetas(entityType, entityId, userId, true);
      for (const [metaKey, metaValue] of Object.entries(meta)) {
        if (metaValue !== null && metaValue !== void 0) {
          try {
            const existingValue = existingMetadata[metaKey];
            let finalValue;
            if (metaValue && typeof metaValue === "object" && !Array.isArray(metaValue) && existingValue && typeof existingValue === "object" && !Array.isArray(existingValue)) {
              finalValue = {
                ...existingValue,
                ...metaValue
              };
            } else {
              finalValue = metaValue;
            }
            const detectedType = detectMetaDataType(finalValue);
            let typedValue = finalValue;
            if (detectedType === "number" && typeof finalValue === "string") {
              typedValue = Number(finalValue);
            } else if (detectedType === "boolean" && typeof finalValue === "string") {
              typedValue = finalValue === "true";
            }
            const metaOptions = {
              isPublic: true,
              // Default to public for API-set metas
              isSearchable: false,
              dataType: detectedType
            };
            await MetaService.setEntityMeta(entityType, entityId, metaKey, typedValue, userId, metaOptions);
          } catch (individualMetaError) {
            console.error(`Error setting ${entityType} metadata for key '${metaKey}':`, individualMetaError);
            errors.push(`Failed to set metadata for key '${metaKey}'`);
            success = false;
          }
        }
      }
    } catch (metaError) {
      console.error(`Error processing ${entityType} metadata:`, metaError);
      errors.push("General metadata processing error");
      success = false;
    }
  }
  return { success, errors };
}
export {
  addCorsHeaders,
  checkScope,
  createApiError,
  createApiResponse,
  createPaginationMeta,
  getApiAuth,
  handleCorsPreflightRequest,
  handleEntityMetadataInResponse,
  includeEntityChildren,
  includeEntityMetadata,
  includeEntityMetadataForSingle,
  isValidUUID,
  logApiUsage,
  parseChildParams,
  parseFilters,
  parseMetaParams,
  parsePaginationParams,
  processEntityMetadata,
  sanitizeSearchQuery,
  validateAndAuthenticateApiRequest,
  validateAndAuthenticateRequest,
  withApiLogging,
  wrapAuthHandlerWithCors
};
