// api.ts から分割（挙動不変）: スペース（CRUD・メンバー・招待・ツールポリシー・Python パッケージ）。
import { BASE } from './client';

// --- Spaces (個人/案件) ---------------------------------------------------

export interface Space {
  id: string;
  kind: 'personal' | 'case';
  title: string;
  description: string;
  ownerId: string | null;
  visibility: 'private' | 'org' | 'public';
  visibilityScopeOrgId: string | null;
  status: 'open' | 'archived';
  brandColor: string | null;
  workspaceDir: string | null;
  createdAt: string;
  updatedAt: string;
  /** 閲覧者自身のメンバーロール。一覧（GET /spaces）・詳細（GET /spaces/:id）の両方で付与。
   *  非メンバー（根オーナー含む。owner_id はメンバー行ではない）は null。 */
  myRole?: SpaceMemberRole | null;
}

export async function fetchSpaces(): Promise<Space[]> {
  const res = await fetch(`${BASE}/local/spaces`);
  const data = await res.json();
  if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch spaces');
  return data as Space[];
}

export type SpaceMemberRole = 'owner' | 'editor' | 'viewer';

export interface SpaceMember {
  userId: string;
  name: string | null;
  email: string | null;
  avatarUrl: string | null;
  role: SpaceMemberRole;
  isOwner: boolean;
}

export async function fetchSpaceMembers(spaceId: string): Promise<SpaceMember[]> {
  const res = await fetch(`${BASE}/local/spaces/${spaceId}/members`);
  const data = await res.json();
  if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch members');
  return data as SpaceMember[];
}

export async function addSpaceMember(
  spaceId: string,
  input: { userId: string; role: SpaceMemberRole },
): Promise<void> {
  const res = await fetch(`${BASE}/local/spaces/${spaceId}/members`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(input),
  });
  if (!res.ok) {
    const d = await res.json().catch(() => ({}));
    throw new Error(d?.error ?? 'Failed to add member');
  }
}

export async function updateSpaceMemberRole(
  spaceId: string,
  userId: string,
  role: SpaceMemberRole,
): Promise<void> {
  const res = await fetch(`${BASE}/local/spaces/${spaceId}/members/${encodeURIComponent(userId)}`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ role }),
  });
  if (!res.ok) {
    const d = await res.json().catch(() => ({}));
    throw new Error(d?.error ?? 'Failed to update member role');
  }
}

export async function removeSpaceMember(spaceId: string, userId: string): Promise<void> {
  const res = await fetch(`${BASE}/local/spaces/${spaceId}/members/${encodeURIComponent(userId)}`, {
    method: 'DELETE',
  });
  if (!res.ok) {
    const d = await res.json().catch(() => ({}));
    throw new Error(d?.error ?? 'Failed to remove member');
  }
}

// ─── ワークスペース招待リンク（再利用トークン）──────────────────────
export type SpaceInviteRole = 'editor' | 'viewer';

export interface SpaceInviteInfo {
  token: string;
  /** 相対パス。origin を前置して共有する。 */
  url: string;
  role: SpaceInviteRole;
  createdAt: string;
  expiresAt: string | null;
  revokedAt: string | null;
  valid: boolean;
}

/** 現行の招待リンク（canManageSpace）。無ければ null。 */
export async function fetchSpaceInvite(spaceId: string): Promise<SpaceInviteInfo | null> {
  const res = await fetch(`${BASE}/local/spaces/${spaceId}/invite`);
  const data = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch invite');
  return (data?.invite ?? null) as SpaceInviteInfo | null;
}

/** 招待リンクを (再)生成する（canManageSpace）。 */
export async function createSpaceInvite(
  spaceId: string,
  input: { role: SpaceInviteRole; expiresInDays?: number | null },
): Promise<SpaceInviteInfo> {
  const res = await fetch(`${BASE}/local/spaces/${spaceId}/invite`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(input),
  });
  const data = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(data?.error ?? 'Failed to create invite');
  return data.invite as SpaceInviteInfo;
}

/** 招待リンクを無効化する（canManageSpace）。 */
export async function revokeSpaceInvite(spaceId: string): Promise<void> {
  const res = await fetch(`${BASE}/local/spaces/${spaceId}/invite`, { method: 'DELETE' });
  if (!res.ok && res.status !== 204) {
    const d = await res.json().catch(() => ({}));
    throw new Error(d?.error ?? 'Failed to revoke invite');
  }
}

export interface InvitePreview {
  spaceId: string;
  spaceTitle: string;
  role: SpaceInviteRole;
}

/**
 * 招待トークンのプレビュー。`status` で分岐を呼び出し側に渡す:
 *   'ok' → preview / 'unauthorized'（要ログイン）/ 'invalid'（無効・期限切れ・不明）。
 */
export async function fetchInvitePreview(
  token: string,
): Promise<{ status: 'ok'; preview: InvitePreview } | { status: 'unauthorized' | 'invalid' }> {
  const res = await fetch(`${BASE}/local/spaces/invite/${encodeURIComponent(token)}`);
  if (res.status === 401) return { status: 'unauthorized' };
  if (!res.ok) return { status: 'invalid' };
  const data = await res.json();
  return { status: 'ok', preview: data as InvitePreview };
}

/** 招待を受諾して参加する。参加先 spaceId を返す。 */
export async function acceptSpaceInvite(token: string): Promise<{ spaceId: string; alreadyMember: boolean }> {
  const res = await fetch(`${BASE}/local/spaces/invite/${encodeURIComponent(token)}/accept`, {
    method: 'POST',
  });
  const data = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(data?.error ?? 'Failed to accept invite');
  return data as { spaceId: string; alreadyMember: boolean };
}


// ─── ワークスペース ツールポリシー ────────────────────────────────────────

export interface ToolCategory {
  name: string;
  sensitive: boolean;
  enabled: boolean;
}

export interface SpaceToolPolicy {
  disabledSafe: string[];
  enabledSensitive: string[];
}

export interface SpaceToolPolicyResponse {
  policy: SpaceToolPolicy;
  categories: ToolCategory[];
  sensitiveTools: { name: string; enabled: boolean }[];
}

export async function fetchSpaceToolPolicy(spaceId: string): Promise<SpaceToolPolicyResponse> {
  const res = await fetch(`${BASE}/local/spaces/${spaceId}/tool-policy`);
  const data = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch tool policy');
  return data as SpaceToolPolicyResponse;
}

export async function updateSpaceToolPolicy(
  spaceId: string,
  patch: { disabledSafe?: string[]; enabledSensitive?: string[] },
): Promise<void> {
  const res = await fetch(`${BASE}/local/spaces/${spaceId}/tool-policy`, {
    method: 'PUT',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(patch),
  });
  if (!res.ok) {
    const d = await res.json().catch(() => ({}));
    throw new Error(d?.error ?? 'Failed to update tool policy');
  }
}

// ─── Per-space Python packages ────────────────────────────────────────────
export interface SpacePythonPackage {
  name: string;
  spec: string;
  addedAt: string;
}
export interface SpacePythonPackagesResponse {
  enabled: boolean;
  maxPackagesPerSpace: number;
  preflight: { ok: boolean; reason?: string };
  packages: SpacePythonPackage[];
}

export async function fetchSpacePythonPackages(spaceId: string): Promise<SpacePythonPackagesResponse> {
  const res = await fetch(`${BASE}/local/spaces/${spaceId}/python-packages`);
  const data = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(data?.error ?? 'Failed to fetch python packages');
  return data as SpacePythonPackagesResponse;
}

export async function addSpacePythonPackage(
  spaceId: string,
  spec: string,
): Promise<{ packages: SpacePythonPackage[] }> {
  const res = await fetch(`${BASE}/local/spaces/${spaceId}/python-packages`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ spec }),
  });
  const data = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(data?.error ?? 'Failed to add package');
  return data as { packages: SpacePythonPackage[] };
}

export async function removeSpacePythonPackage(
  spaceId: string,
  name: string,
): Promise<{ packages: SpacePythonPackage[] }> {
  const res = await fetch(`${BASE}/local/spaces/${spaceId}/python-packages/${encodeURIComponent(name)}`, {
    method: 'DELETE',
  });
  const data = await res.json().catch(() => ({}));
  if (!res.ok) throw new Error(data?.error ?? 'Failed to remove package');
  return data as { packages: SpacePythonPackage[] };
}

export async function createSpace(input: {
  title: string;
  description?: string;
  brandColor?: string | null;
  visibility?: Space['visibility'];
}): Promise<Space> {
  const res = await fetch(`${BASE}/local/spaces`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(input),
  });
  const data = await res.json();
  if (!res.ok) throw new Error(data?.error ?? 'Failed to create space');
  return data as Space;
}

export async function updateSpace(
  id: string,
  patch: Partial<Pick<Space, 'title' | 'description' | 'brandColor' | 'visibility'>>,
): Promise<Space> {
  const res = await fetch(`${BASE}/local/spaces/${id}`, {
    method: 'PATCH',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(patch),
  });
  const data = await res.json();
  if (!res.ok) throw new Error(data?.error ?? 'Failed to update space');
  return data as Space;
}

export async function archiveSpace(id: string): Promise<void> {
  const res = await fetch(`${BASE}/local/spaces/${id}/archive`, { method: 'POST' });
  if (!res.ok) {
    const d = await res.json().catch(() => ({}));
    throw new Error(d?.error ?? 'Failed to archive space');
  }
}
