import express, { Router } from 'express';
import { canManageSpace } from './visibility.js';
import { viewerOf } from './space-viewer.js';
import type { SpaceMemberRoleValue } from '../db/repository.js';
import type { SpaceApiDeps } from './space-api.js';

/** スペースのメンバー（協働者）CRUD。createSpaceApi から分離（巨大関数分割）。挙動は等価。 */
export function registerSpaceMembersRoutes(router: Router, deps: SpaceApiDeps): void {
  const { repo } = deps;
  const jsonParser = express.json({ limit: deps.uploadLimitMb ? `${deps.uploadLimitMb}mb` : '50mb' });

  // ─── メンバー（スペースの協働者）─────────────────────────────────────
  //
  // 可視性は他のスペース配下ルートと同じく getSpace({viewer}) が null を返したら 404。
  // 一覧はスペース可視者なら誰でも可（read）。追加/変更/削除は canManageSpace
  // （owner / admin / member.role==='owner'）。自分自身を抜けるのは管理権限が無くても可。
  // owner_id 本人は常に owner として合成して返し、member 行を持つことはできない。

  const VALID_MEMBER_ROLES: readonly SpaceMemberRoleValue[] = ['owner', 'editor', 'viewer'];
  const isValidRole = (r: unknown): r is SpaceMemberRoleValue =>
    typeof r === 'string' && (VALID_MEMBER_ROLES as readonly string[]).includes(r);

  // 一覧（スペース可視者なら誰でも）。owner_id を owner として合成し、member 行を後続。
  router.get('/:id/members', async (req, res) => {
    const viewer = viewerOf(req, deps.authActive);
    const space = await repo.getSpace(req.params.id, { viewer });
    if (!space) return res.status(404).json({ error: 'not found' });

    const members = await repo.listSpaceMembers(space.id);

    // email は PII。public/org スペースでは getSpace を通る非メンバー閲覧者にも
    // メンバー一覧が見えるため、email は「資格者」(admin / owner / メンバー本人) に
    // だけ返す。それ以外には name + avatar のみ（メールアドレス収集ベクター対策）。
    const viewerEntitled =
      viewer.role === 'admin' ||
      space.ownerId === viewer.id ||
      (await repo.getSpaceMemberRole(space.id, viewer.id)) !== null;
    const maskEmail = (email: string | null) => (viewerEntitled ? email : null);

    const out: Array<{
      userId: string;
      name: string | null;
      email: string | null;
      avatarUrl: string | null;
      role: SpaceMemberRoleValue;
      isOwner: boolean;
    }> = [];

    // owner_id を合成 owner として先頭に置く（行が重複しても owner 行が勝つ）。
    if (space.ownerId) {
      const ownerUser = repo.getUserById(space.ownerId);
      out.push({
        userId: space.ownerId,
        name: ownerUser?.name ?? null,
        email: maskEmail(ownerUser?.email ?? null),
        avatarUrl: ownerUser?.avatarUrl ?? null,
        role: 'owner',
        isOwner: true,
      });
    }
    for (const m of members) {
      if (space.ownerId && m.userId === space.ownerId) continue; // owner 行が勝つ → de-dupe
      out.push({
        userId: m.userId,
        name: m.name,
        email: maskEmail(m.email),
        avatarUrl: m.avatarUrl,
        role: m.role,
        isOwner: false,
      });
    }
    res.json(out);
  });

  // 追加 / 招待（canManageSpace）。owner_id 本人は追加不可。未知ユーザー・不正ロールは 400。
  router.post('/:id/members', jsonParser, async (req, res) => {
    const viewer = viewerOf(req, deps.authActive);
    const space = await repo.getSpace(req.params.id, { viewer });
    if (!space) return res.status(404).json({ error: 'not found' });
    const memberRole = repo.getSpaceMemberRole(space.id, viewer.id);
    if (!canManageSpace(viewer, { ownerId: space.ownerId }, memberRole)) {
      return res.status(403).json({ error: 'forbidden' });
    }
    const userId = (req.body?.userId ?? '').toString();
    const role = req.body?.role;
    if (!userId) return res.status(400).json({ error: 'userId is required' });
    if (space.ownerId && userId === space.ownerId) {
      return res.status(400).json({ error: 'user is already the space owner' });
    }
    if (!isValidRole(role)) return res.status(400).json({ error: 'invalid role' });
    if (!repo.getUserById(userId)) return res.status(400).json({ error: 'unknown user' });

    await repo.addSpaceMember({ spaceId: space.id, userId, role, invitedBy: viewer.id });
    res.status(201).json({ userId, role });
  });

  // ロール変更（canManageSpace）。メンバーでなければ 404、不正ロールは 400。
  router.patch('/:id/members/:userId', jsonParser, async (req, res) => {
    const viewer = viewerOf(req, deps.authActive);
    const space = await repo.getSpace(req.params.id, { viewer });
    if (!space) return res.status(404).json({ error: 'not found' });
    const memberRole = repo.getSpaceMemberRole(space.id, viewer.id);
    if (!canManageSpace(viewer, { ownerId: space.ownerId }, memberRole)) {
      return res.status(403).json({ error: 'forbidden' });
    }
    const targetUserId = req.params.userId;
    const role = req.body?.role;
    if (!isValidRole(role)) return res.status(400).json({ error: 'invalid role' });
    if (repo.getSpaceMemberRole(space.id, targetUserId) === null) {
      return res.status(404).json({ error: 'member not found' });
    }
    await repo.updateSpaceMemberRole(space.id, targetUserId, role);
    res.json({ userId: targetUserId, role });
  });

  // 除去（canManageSpace、または自分自身を抜ける場合）。メンバーでなければ 404。
  router.delete('/:id/members/:userId', async (req, res) => {
    const viewer = viewerOf(req, deps.authActive);
    const space = await repo.getSpace(req.params.id, { viewer });
    if (!space) return res.status(404).json({ error: 'not found' });
    const targetUserId = req.params.userId;
    const memberRole = repo.getSpaceMemberRole(space.id, viewer.id);
    const isSelfRemoval = targetUserId === viewer.id;
    if (!isSelfRemoval && !canManageSpace(viewer, { ownerId: space.ownerId }, memberRole)) {
      return res.status(403).json({ error: 'forbidden' });
    }
    if (repo.getSpaceMemberRole(space.id, targetUserId) === null) {
      return res.status(404).json({ error: 'member not found' });
    }
    await repo.removeSpaceMember(space.id, targetUserId);
    res.status(204).end();
  });

}
