import type { FastifyInstance } from 'fastify';
import { z } from 'zod';
import { env } from '../config/env.js';
import {
  deleteSharedResourceTokens,
  deleteSharedResourceToken,
  getSharedResourceTokenDetail,
  getSharedResourceTokenDetailBySharePath,
  listSharedResourceTokens,
  recordSharedResourceTokenUsage,
  restoreSharedResourceToken,
  revokeSharedResourceToken,
  revokeSharedResourceTokens,
  sharedResourceTokenSchema,
  upsertSharedResourceToken,
} from '../services/shared-resource-token-service.js';
import { extractRequestAuditContext } from '../utils/request-audit.js';

function getRequestAccessToken(request: { headers: Record<string, string | string[] | undefined> }) {
  const tokenHeader = request.headers['x-access-token'];
  return Array.isArray(tokenHeader) ? tokenHeader[0]?.trim() ?? '' : String(tokenHeader ?? '').trim();
}

function getRequestChatShareToken(request: { headers: Record<string, string | string[] | undefined> }) {
  const tokenHeader = request.headers['x-chat-share-token'];
  return Array.isArray(tokenHeader) ? tokenHeader[0]?.trim() ?? '' : String(tokenHeader ?? '').trim();
}

function resolveChatSharePath(token: string) {
  return `/chat/share/${encodeURIComponent(token)}`;
}

type SharedResourceTokenAccessContext =
  | { scope: 'full' }
  | { scope: 'shared'; tokenId: string };

async function resolveSharedResourceTokenAccessContext(
  request: { headers: Record<string, string | string[] | undefined> },
) {
  if (getRequestAccessToken(request) === env.SERVER_COMMAND_ACCESS_TOKEN) {
    return { scope: 'full' } satisfies SharedResourceTokenAccessContext;
  }

  const shareToken = getRequestChatShareToken(request);
  if (!shareToken) {
    return null;
  }

  const managedResource = await getSharedResourceTokenDetailBySharePath(resolveChatSharePath(shareToken));
  if (!managedResource || managedResource.token.enabled === false || managedResource.token.revokedAt) {
    return null;
  }

  const allowedAppIds = managedResource.token.allowedAppIds ?? [];
  const normalizedAllowedAppIds = new Set(allowedAppIds.map((item) => item.trim().toLowerCase()).filter(Boolean));

  if (!managedResource.token.permissions.includes('manage') || !normalizedAllowedAppIds.has('shared-resource')) {
    return null;
  }

  return {
    scope: 'shared',
    tokenId: managedResource.token.id,
  } satisfies SharedResourceTokenAccessContext;
}

function sendAccessDenied(
  reply: { code: (statusCode: number) => { send: (payload: { message: string }) => unknown } },
) {
  reply.code(403).send({
    message: '권한 토큰 또는 shared-resource 관리 권한이 있는 공유 링크에서만 공유 리소스 관리를 사용할 수 있습니다.',
  });
}

function isAllowedSharedTokenTarget(accessContext: SharedResourceTokenAccessContext, tokenId: string) {
  return accessContext.scope === 'full' || accessContext.tokenId === tokenId;
}

export async function registerSharedResourceTokenRoutes(app: FastifyInstance) {
  app.get('/api/shared-resource-tokens', async (request, reply) => {
    const accessContext = await resolveSharedResourceTokenAccessContext(request);
    if (!accessContext) {
      sendAccessDenied(reply);
      return;
    }

    const sharedTokenDetail =
      accessContext.scope === 'shared' ? await getSharedResourceTokenDetail(accessContext.tokenId) : null;
    const items =
      accessContext.scope === 'full'
        ? await listSharedResourceTokens()
        : sharedTokenDetail?.token
          ? [sharedTokenDetail.token]
          : [];

    return {
      ok: true,
      items,
    };
  });

  app.get('/api/shared-resource-tokens/:tokenId', async (request, reply) => {
    const accessContext = await resolveSharedResourceTokenAccessContext(request);
    if (!accessContext) {
      sendAccessDenied(reply);
      return;
    }

    const tokenId = z.string().trim().min(1).parse((request.params as { tokenId: string }).tokenId);
    if (!isAllowedSharedTokenTarget(accessContext, tokenId)) {
      return reply.code(403).send({
        message: '현재 공유 링크로는 이 공유 토큰 상세를 열 수 없습니다.',
      });
    }

    const item = await getSharedResourceTokenDetail(tokenId);

    if (!item) {
      return reply.code(404).send({
        message: '공유 리소스 토큰을 찾을 수 없습니다.',
      });
    }

    return {
      ok: true,
      ...item,
    };
  });

  app.put('/api/shared-resource-tokens', async (request, reply) => {
    const accessContext = await resolveSharedResourceTokenAccessContext(request);
    if (!accessContext) {
      sendAccessDenied(reply);
      return;
    }

    try {
      const payload = sharedResourceTokenSchema.parse(request.body ?? {});

      if (accessContext.scope === 'shared' && payload.id !== accessContext.tokenId) {
        return reply.code(403).send({
          message: '현재 공유 링크에서는 연결된 공유 토큰 상세만 수정할 수 있습니다.',
        });
      }

      const saved = await upsertSharedResourceToken(payload, {
        actorLabel: accessContext.scope === 'full' ? 'manager' : 'shared-link-manager',
        audit: extractRequestAuditContext(request),
      });
      return {
        ok: true,
        ...saved,
      };
    } catch (error) {
      return reply.code(409).send({
        message: error instanceof Error ? error.message : '공유 리소스 토큰 저장에 실패했습니다.',
      });
    }
  });

  app.post('/api/shared-resource-tokens/bulk-revoke', async (request, reply) => {
    const accessContext = await resolveSharedResourceTokenAccessContext(request);
    if (!accessContext) {
      sendAccessDenied(reply);
      return;
    }

    const payload = z
      .object({
        tokenIds: z.array(z.string().trim().min(1)).min(1).max(500),
        reason: z.string().trim().max(500).optional().nullable(),
      })
      .parse(request.body ?? {});

    if (accessContext.scope === 'shared' && payload.tokenIds.some((tokenId) => tokenId !== accessContext.tokenId)) {
      return reply.code(403).send({
        message: '현재 공유 링크에서는 연결된 공유 토큰만 회수할 수 있습니다.',
      });
    }

    const result = await revokeSharedResourceTokens(payload.tokenIds, payload.reason, {
      actorLabel: accessContext.scope === 'full' ? 'manager' : 'shared-link-manager',
      audit: extractRequestAuditContext(request),
    });

    return {
      ok: true,
      ...result,
    };
  });

  app.post('/api/shared-resource-tokens/:tokenId/revoke', async (request, reply) => {
    const accessContext = await resolveSharedResourceTokenAccessContext(request);
    if (!accessContext) {
      sendAccessDenied(reply);
      return;
    }

    const tokenId = z.string().trim().min(1).parse((request.params as { tokenId: string }).tokenId);
    if (!isAllowedSharedTokenTarget(accessContext, tokenId)) {
      return reply.code(403).send({
        message: '현재 공유 링크에서는 연결된 공유 토큰만 회수할 수 있습니다.',
      });
    }

    const payload = z
      .object({
        reason: z.string().trim().max(500).optional().nullable(),
      })
      .parse(request.body ?? {});
    const saved = await revokeSharedResourceToken(tokenId, payload.reason, {
      actorLabel: accessContext.scope === 'full' ? 'manager' : 'shared-link-manager',
      audit: extractRequestAuditContext(request),
    });

    if (!saved) {
      return reply.code(404).send({
        message: '회수할 공유 리소스 토큰을 찾을 수 없습니다.',
      });
    }

    return {
      ok: true,
      ...saved,
    };
  });

  app.post('/api/shared-resource-tokens/:tokenId/restore', async (request, reply) => {
    const accessContext = await resolveSharedResourceTokenAccessContext(request);
    if (!accessContext) {
      sendAccessDenied(reply);
      return;
    }

    const tokenId = z.string().trim().min(1).parse((request.params as { tokenId: string }).tokenId);
    if (!isAllowedSharedTokenTarget(accessContext, tokenId)) {
      return reply.code(403).send({
        message: '현재 공유 링크에서는 연결된 공유 토큰만 복원할 수 있습니다.',
      });
    }

    const saved = await restoreSharedResourceToken(tokenId, {
      actorLabel: accessContext.scope === 'full' ? 'manager' : 'shared-link-manager',
      audit: extractRequestAuditContext(request),
    });

    if (!saved) {
      return reply.code(404).send({
        message: '복원할 공유 리소스 토큰을 찾을 수 없습니다.',
      });
    }

    return {
      ok: true,
      ...saved,
    };
  });

  app.post('/api/shared-resource-tokens/:tokenId/usage', async (request, reply) => {
    const accessContext = await resolveSharedResourceTokenAccessContext(request);
    if (!accessContext) {
      sendAccessDenied(reply);
      return;
    }

    const tokenId = z.string().trim().min(1).parse((request.params as { tokenId: string }).tokenId);
    if (!isAllowedSharedTokenTarget(accessContext, tokenId)) {
      return reply.code(403).send({
        message: '현재 공유 링크에서는 연결된 공유 토큰만 기록할 수 있습니다.',
      });
    }

    const payload = z
      .object({
        actorLabel: z.string().trim().max(120).optional().nullable(),
        summary: z.string().trim().max(400).optional().nullable(),
        detail: z.string().trim().max(2000).optional().nullable(),
        usageDelta: z.number().int().min(1).max(1_000_000).optional().nullable(),
      })
      .parse(request.body ?? {});
    const saved = await recordSharedResourceTokenUsage(tokenId, {
      ...payload,
      audit: extractRequestAuditContext(request),
    });

    if (!saved) {
      return reply.code(404).send({
        message: '사용량을 기록할 공유 리소스 토큰을 찾을 수 없습니다.',
      });
    }

    return {
      ok: true,
      ...saved,
    };
  });

  app.post('/api/shared-resource-tokens/bulk-delete', async (request, reply) => {
    const accessContext = await resolveSharedResourceTokenAccessContext(request);
    if (!accessContext) {
      sendAccessDenied(reply);
      return;
    }

    const payload = z
      .object({
        tokenIds: z.array(z.string().trim().min(1)).min(1).max(500),
      })
      .parse(request.body ?? {});

    if (accessContext.scope === 'shared' && payload.tokenIds.some((tokenId) => tokenId !== accessContext.tokenId)) {
      return reply.code(403).send({
        message: '현재 공유 링크에서는 연결된 공유 토큰만 삭제할 수 있습니다.',
      });
    }

    const result = await deleteSharedResourceTokens(payload.tokenIds, {
      actorLabel: accessContext.scope === 'full' ? 'manager' : 'shared-link-manager',
      audit: extractRequestAuditContext(request),
    });

    return {
      ok: true,
      ...result,
    };
  });

  app.delete('/api/shared-resource-tokens/:tokenId', async (request, reply) => {
    const accessContext = await resolveSharedResourceTokenAccessContext(request);
    if (!accessContext) {
      sendAccessDenied(reply);
      return;
    }

    const tokenId = z.string().trim().min(1).parse((request.params as { tokenId: string }).tokenId);
    if (!isAllowedSharedTokenTarget(accessContext, tokenId)) {
      return reply.code(403).send({
        message: '현재 공유 링크에서는 연결된 공유 토큰만 삭제할 수 있습니다.',
      });
    }

    const deleted = await deleteSharedResourceToken(tokenId, {
      actorLabel: accessContext.scope === 'full' ? 'manager' : 'shared-link-manager',
      audit: extractRequestAuditContext(request),
    });

    if (!deleted) {
      return reply.code(404).send({
        message: '삭제할 공유 리소스 토큰을 찾을 수 없습니다.',
      });
    }

    return {
      ok: true,
      deleted: true,
      tokenId,
    };
  });
}
