export type Role = 'user' | 'assistant';

export type MessageContent =
  | { type: 'text'; text: string }
  | { type: 'thinking'; thinking: string; signature?: string }
  | {
      type: 'systemNotification';
      notificationType: string;
      msg: string;
      data?: Record<string, unknown>;
    }
  | { type: 'toolRequest'; id: string; toolCall: { functionName: string; arguments: unknown } }
  | { type: 'toolResponse'; id: string; toolResult: { value: unknown } }
  | {
      type: 'actionRequired';
      data: {
        actionType: 'toolConfirmation';
        id: string;
        toolName: string;
        arguments: Record<string, unknown>;
        prompt?: string | null;
      };
    };

export type Message = {
  id?: string | null;
  role: Role;
  created: number;
  content: MessageContent[];
  metadata: {
    userVisible: boolean;
    agentVisible: boolean;
    displayText?: string;
    imageUrls?: string[];
  };
};

export type MindSpaceChatView = 'home' | 'category' | 'page';

export type MindSpaceChatContext = {
  spaceId: string;
  spaceName: string;
  ownerUsername?: string;
  view: MindSpaceChatView;
  route: string;
  category?: {
    code: MindSpaceCategory['code'];
    name: string;
    itemCount?: number;
    assets?: Array<{
      id: string;
      displayName: string;
      mimeType: string;
    }>;
  };
  page?: {
    id: string;
    title: string;
    summary?: string;
    status?: string;
    versionNo?: number;
    categoryCode?: MindSpaceCategory['code'];
    templateId?: string;
    contentFormat?: MindSpaceContentFormat;
    pageType?: string;
    contentExcerpt?: string;
    publicationUrl?: string | null;
  };
  focusedAsset?: {
    id: string;
    displayName: string;
    mimeType: string;
  };
  homeCategories?: Array<{
    code: MindSpaceCategory['code'];
    name: string;
    itemCount: number;
  }>;
  recentPages?: Array<{
    id: string;
    title: string;
    status: string;
    categoryCode: MindSpaceCategory['code'];
  }>;
  agentSessionId?: string;
  h5ApiBase?: string;
  pageEditMode?: boolean;
  parentAgentSessionId?: string;
};

export type TokenState = {
  inputTokens: number;
  outputTokens: number;
  totalTokens: number;
  accumulatedInputTokens?: number;
  accumulatedOutputTokens?: number;
  accumulatedTotalTokens?: number;
  accumulatedCost?: number | null;
};

export type SessionEvent =
  | { type: 'Message'; message: Message; token_state: TokenState }
  | { type: 'Finish'; reason: string; token_state: TokenState }
  | { type: 'Error'; error: string }
  | { type: 'UpdateConversation'; conversation: Message[] }
  | { type: 'Ping' }
  | { type: 'ActiveRequests'; request_ids: string[] };

export type Session = {
  id: string;
  name: string;
  working_dir: string;
  message_count: number;
  created_at?: string;
  updated_at?: string;
  user_set_name?: boolean;
  recipe?: { title?: string | null } | null;
  conversation?: Message[] | null;
};

export type SessionListResponse = {
  sessions: Session[];
};

export type HarnessBootstrapResponse = {
  summary: string;
  source: string;
  refreshed: boolean;
};

export type ChatState = 'idle' | 'loading' | 'connecting' | 'streaming' | 'waiting' | 'error';

export type ToolConfirmation = {
  id: string;
  toolName: string;
  arguments: Record<string, unknown>;
  prompt?: string | null;
};

export type BillingConfig = {
  wechatEnabled: boolean;
  tiersCents: number[];
  minRechargeCents: number;
  balanceCents: number;
};

export type UserNotification = {
  id: string;
  userId: string;
  channel: 'web' | 'wechat';
  notificationType: string;
  title: string;
  body: string;
  data?: Record<string, unknown> | null;
  status: 'unread' | 'read';
  readAt: number | null;
  createdAt: number;
  updatedAt: number;
};

export type JsapiPayParams = {
  appId: string;
  timeStamp: string;
  nonceStr: string;
  package: string;
  signType: 'MD5' | 'RSA';
  paySign: string;
};

export type RechargeOrder = {
  id: string;
  amountCents: number;
  status: 'pending' | 'paid' | 'failed' | 'expired' | 'refunded';
  payMode: 'native' | 'h5' | 'jsapi';
  expireAt: number;
  codeUrl?: string | null;
  h5Url?: string | null;
  jsapiParams?: JsapiPayParams | null;
};

export type InsufficientBalanceDetails = {
  code: 'INSUFFICIENT_BALANCE';
  balanceCents: number;
  minRechargeCents: number;
  suggestedTiers: number[];
};

export type ActiveSubscription = {
  id: string;
  planType: string;
  status: 'active' | 'expired' | 'cancelled';
  periodTokensLimit: number;
  periodTokensUsed: number;
  periodStart: number;
  periodEnd: number;
  expiresAt: number;
  overageRate: number;
  autoRenew: boolean;
};

export type PlanDefinition = {
  key: string;
  name: string;
  priceCents: number;
  periodDays: number;
  periodTokens: number;
  modelTier: string;
  overageRate: number;
};

export type PortalUser = {
  id: string;
  username: string;
  slug?: string;
  email?: string | null;
  displayName: string;
  role: 'user' | 'admin';
  status: 'active' | 'suspended' | 'disabled';
  planType?: string;
  workspaceRoot: string;
  balanceCents: number;
  totalCreditCents?: number;
  tokensUsed: number;
  subscription?: ActiveSubscription | null;
  spaceQuotaBytes?: number;
  spaceUsedBytes?: number;
  spaceReservedBytes?: number;
  spaceAvailableBytes?: number;
  publishSlug?: string;
  publishUrl?: string;
  publishSkillName?: string;
};

export type MindSpaceQuota = {
  quotaBytes: number;
  usedBytes: number;
  reservedBytes: number;
  availableBytes: number;
  maxFileBytes: number;
  publicPageLimit: number;
  publicPageUsed: number;
  aiDailyLimit: number;
  aiDailyUsed: number;
  monthlyViewLimit: number;
  monthlyViewUsed: number;
};

export type MindSpaceCategory = {
  id: string;
  code: 'oa' | 'private' | 'public' | 'draft' | 'archive';
  name: string;
  visibilityPolicy: string;
  aiAccessPolicy: string;
  publishPolicy: string;
  isSystem: boolean;
  sortOrder: number;
  itemCount: number;
};

export type MindSpaceScheduleItem = {
  id: string;
  kind: 'task' | 'event';
  title: string;
  description?: string | null;
  status: 'active' | 'completed' | 'cancelled' | 'deleted';
  startAt?: number | null;
  endAt?: number | null;
  dueAt?: number | null;
  allDay: boolean;
  timezone: string;
  location?: string | null;
  sourceChannel?: 'h5' | 'wechat' | 'agent' | 'api';
  sourceMessageId?: string | null;
  sourceText?: string | null;
  createdAt: number;
  updatedAt: number;
};

export type MindSpaceDigestSubscription = {
  id: string;
  hour: number;
  minute: number;
  timezone: string;
  channel: 'wechat' | 'in_app';
  status: 'active' | 'locked' | 'failed' | 'cancelled';
  nextRunAt: number;
  lastRunAt?: number | null;
  attempts?: number;
};

export type MindSpaceSchedule = {
  todayTodoItems: MindSpaceScheduleItem[];
  digestSubscriptions: MindSpaceDigestSubscription[];
};

export type MindSpace = {
  id: string;
  userId: string;
  name: string;
  status: 'active' | 'locked' | 'deleted';
  quota: MindSpaceQuota;
  categories: MindSpaceCategory[];
  schedule?: MindSpaceSchedule | null;
  createdAt: number;
  updatedAt: number;
};

export type MindSpaceAsset = {
  id: string;
  categoryId: string;
  categoryCode: MindSpaceCategory['code'];
  assetType: string;
  mimeType: string;
  filename: string;
  displayName: string;
  sizeBytes: number;
  checksum: string;
  riskLevel: 'none' | 'low' | 'medium' | 'high' | 'critical';
  visibility: 'private' | 'internal' | 'public_candidate';
  status: 'uploaded' | 'processing' | 'ready' | 'quarantined' | 'archived';
  scanStatus?: 'pending' | 'passed' | 'warned' | 'blocked';
  sourceType: 'upload' | 'chat' | 'agent' | 'template' | 'generated' | 'workspace';
  hasThumbnail?: boolean;
  publicUrl?: string | null;
  sourcePageId?: string | null;
  createdAt: number;
  updatedAt: number;
};

export type MindSpaceUpload = {
  id: string;
  filename: string;
  expectedSize: number;
  uploadUrl: string;
  expiresAt: number;
  reservedBytes: number;
};

export type MindSpacePageVersion = {
  id: string;
  versionNo: number;
  changeNote: string | null;
  immutable: boolean;
  createdAt: number;
};

export type MindSpaceSecurityFinding = {
  id: string;
  type: string;
  riskLevel: 'low' | 'medium' | 'high' | 'critical';
  occurrenceCount: number;
  sampleMasked: string;
  blocking: boolean;
};

export type MindSpacePublishCheck = {
  pageVersionId: string;
  urlSlug: string;
  accessMode:
    | 'public'
    | 'password'
    | 'private_link'
    | 'time_limited'
    | 'login_required'
    | 'owner_only';
  expiresAt: number | null;
  slugAvailable: boolean;
  slugConflict?: { pageId: string; title: string } | null;
  suggestedUrlSlug?: string | null;
  status: 'passed' | 'warned' | 'blocked';
  riskLevel: 'none' | 'low' | 'medium' | 'high' | 'critical';
  findings: MindSpaceSecurityFinding[];
  allowed: boolean;
};

export type MindSpaceRedactionChange = {
  field: 'title' | 'summary' | 'content';
  fieldLabel: string;
  type: string;
  label: string;
  count: number;
};

export type MindSpaceRedactedCopyResult = {
  page: MindSpacePage;
  originalScan: MindSpacePrivacyScan;
  redactedScan: MindSpacePrivacyScan;
  redactionsApplied: number;
  changes: MindSpaceRedactionChange[];
};

export type MindSpaceCleanupItem = {
  id: string;
  kind: 'stale_upload' | 'orphan_tmp' | 'workspace_temp' | 'stale_page_version';
  label: string;
  path: string;
  sizeBytes: number;
  createdAt: number | null;
  detail: string;
};

export type MindSpacePublication = {
  id: string;
  pageId: string;
  pageVersionId: string;
  urlSlug: string;
  publicUrl: string;
  accessMode: MindSpacePublishCheck['accessMode'];
  expiresAt: number | null;
  status: 'online' | 'offline' | 'expired' | 'blocked';
  viewCount: number;
  publishedAt: number;
  offlineAt: number | null;
};

export type MindSpacePublicationStats = {
  publicationId: string;
  status: string;
  totalViews: number;
  publishedAt: number;
  daily: Array<{ day: string; views: number }>;
  devices: Array<{ deviceType: string; views: number }>;
  sources: Array<{ source: string; views: number }>;
};

export type PlazaCategory = {
  id: string;
  name: string;
  slug: string;
  icon: string;
  post_count: number;
};

export type PlazaPostBrief = {
  id: string;
  status: string;
};

export type MindSpaceAgentJobAsset = {
  assetId: string;
  assetVersionId: string;
  permission: 'read' | 'write' | 'create_derivative';
  displayName: string;
  mimeType: string;
  status: string;
  scanStatus: string;
};

export type MindSpaceAgentJob = {
  id: string;
  sessionId: string | null;
  jobType: string;
  instruction: string;
  status: 'queued' | 'running' | 'completed' | 'failed' | 'cancelled' | 'timed_out';
  outputType: 'page_draft' | 'html_page' | 'markdown';
  outputCategoryId: string;
  outputCategoryCode: string | null;
  progress: { stage: string; message?: string };
  permissionScope: Record<string, unknown>;
  userContext: { locale?: string; timezone?: string };
  resultPageId: string | null;
  resultAssetId: string | null;
  errorCode: string | null;
  errorMessage: string | null;
  retryable: boolean;
  queuedAt: number;
  startedAt: number | null;
  completedAt: number | null;
  expiresAt: number | null;
  assets: MindSpaceAgentJobAsset[];
};

export type MindSpaceContentFormat = 'markdown' | 'html';

export type MindSpaceSaveCategory = 'draft' | 'oa' | 'private' | 'public';

export type MindSpacePrivacyFinding = {
  id: string;
  type: string;
  label?: string;
  riskLevel: 'low' | 'medium' | 'high' | 'critical';
  occurrenceCount: number;
  sampleMasked: string;
  blocking: boolean;
};

export type MindSpacePrivacyScan = {
  status: 'passed' | 'warned' | 'blocked';
  riskLevel: 'none' | 'low' | 'medium' | 'high' | 'critical';
  findings: MindSpacePrivacyFinding[];
  allowed: boolean;
};

export type ChatSaveContentMode = 'markdown' | 'static_html';

export type ChatSaveAnalysis = {
  contentMode: ChatSaveContentMode;
  links: Array<{
    publicUrl: string;
    owner: string;
    relativePath: string;
    filename: string;
  }>;
  selectedLinkIndex: number;
  suggestedTitle: string;
  suggestedSummary: string;
  previewUrl: string | null;
  previewFrameUrl: string | null;
  localPreviewUrl: string | null;
  localThumbnailUrl: string | null;
  thumbnailUrl: string | null;
  thumbnailReady: boolean;
  relativePath: string | null;
  filename: string | null;
  hasHtmlContent: boolean;
  privacyScan: MindSpacePrivacyScan;
};

export type ChatSaveResult =
  | { kind: 'page'; categoryCode: 'draft'; page: MindSpacePage }
  | { kind: 'asset'; categoryCode: Exclude<MindSpaceSaveCategory, 'draft'>; asset: MindSpaceAsset };

export type MindSpacePage = {
  id: string;
  categoryId: string;
  categoryCode: MindSpaceCategory['code'];
  sourceSessionId: string | null;
  sourceMessageId: string | null;
  sourceAssetId: string | null;
  title: string;
  summary: string;
  pageType: string;
  contentFormat?: MindSpaceContentFormat;
  hasThumbnail?: boolean;
  templateId: string;
  status: 'draft' | 'reviewing' | 'risk_found' | 'ready' | 'published' | 'protected' | 'expired' | 'offline';
  visibility: 'private' | 'internal' | 'public';
  publicationAccessMode?: 'public' | 'password' | 'private_link' | 'time_limited' | 'login_required' | 'owner_only' | null;
  publicationUrl?: string | null;
  currentVersionId: string;
  versionNo: number;
  content?: string;
  versions?: MindSpacePageVersion[];
  publication?: MindSpacePublication | null;
  createdAt: number;
  updatedAt: number;
};

export type MindSpacePageDeletePreview = {
  page: {
    id: string;
    title: string;
    status: MindSpacePage['status'];
    versionCount: number;
  };
  onlinePublication: {
    id: string;
    publicUrl: string;
    accessMode: MindSpacePublication['accessMode'];
    viewCount: number;
  } | null;
  offlinePublicationCount: number;
  linkedAssets: Array<{
    id: string;
    displayName: string;
    sizeBytes: number;
    kind: 'content' | 'bundle';
  }>;
  linkedAssetBytes: number;
  linkedAgentJobCount: number;
  preservesSourceAsset: boolean;
  summaryLines: string[];
};

export type MindSpacePageDeleteResult = {
  deleted: boolean;
  pageId: string;
  title: string;
  offlinedPublicationCount: number;
  deletedAssetCount: number;
  freedBytes: number;
  clearedAgentJobCount: number;
  quota?: MindSpaceQuota;
};

export type MindSpaceRefreshOptions = {
  quota?: MindSpaceQuota;
  freedBytes?: number;
};

export type CapabilityDefinition = {
  key: string;
  label: string;
  description: string;
  risk: 'low' | 'medium' | 'high';
  category: string;
};

export type CapabilityMap = Record<string, boolean>;

export type PolicyValue = string | boolean;

export type PolicyMap = Record<string, PolicyValue>;

export type SkillMap = Record<string, boolean>;

export type SkillDefinition = {
  name: string;
  dirName: string;
  label: string;
  description: string;
  category: string;
  requiresPublish?: boolean;
};

export type PolicyDefinition = {
  key: string;
  label: string;
  description: string;
  type: 'select' | 'boolean';
  options?: Array<{ value: string; label: string }>;
  defaultValue: PolicyValue;
  category: string;
  risk: 'low' | 'medium' | 'high';
};

export type AuthStatus = {
  authenticated: boolean;
  mode?: 'user' | 'legacy' | 'none';
  user?: PortalUser | null;
  capabilities?: CapabilityMap;
  grantedSkills?: string[];
  unrestricted?: boolean;
};

export type PathGrant = {
  path: string;
  mode: 'read' | 'readwrite';
};

export type AdminUserRow = PortalUser & {
  createdAt: number;
  updatedAt: number;
};

export type LlmProviderDefinition = {
  id: string;
  label: string;
  kind?: 'builtin' | 'custom';
  apiKeyEnv: string | null;
  defaultModel: string;
  models: string[];
};

export type LlmProviderKeyRow = {
  id: string;
  providerId: string;
  providerKind: 'builtin' | 'custom';
  providerLabel: string;
  name: string;
  defaultModel: string;
  models: string[];
  apiUrl: string | null;
  basePath: string | null;
  engine: string;
  relayProvider: string | null;
  goosedProviderId: string | null;
  status: 'active' | 'disabled';
  isSelected: boolean;
  apiKeyMasked: string;
  createdAt: number;
  updatedAt: number;
};

export type LlmGlobalSettings = {
  keyId: string | null;
  keyName: string | null;
  providerLabel: string | null;
  globalModel: string | null;
  availableModels: string[];
};

export type LlmConnectionTestResult = {
  ok: boolean;
  latencyMs?: number;
  reply?: string;
  model?: string;
  message?: string;
};

export type UsageRecord = {
  id: number;
  userId: string;
  username: string;
  agentSessionId: string;
  requestId: string | null;
  inputTokens: number;
  outputTokens: number;
  costCents: number;
  balanceAfterCents: number;
  createdAt: number;
};

export type BalanceUpdate = {
  balanceCents: number;
  tokensUsed?: number;
  lastUsage?: Pick<UsageRecord, 'inputTokens' | 'outputTokens' | 'costCents'>;
};

export type LedgerEntry = {
  id: number;
  userId: string;
  username: string;
  type: 'recharge' | 'deduct' | 'refund' | 'adjust';
  amountCents: number;
  tokens: number;
  sessionId: string | null;
  note: string | null;
  createdAt: number;
};

export type AdminDashboardSummary = {
  users: {
    total: number;
    active: number;
    lowBalance: number;
    totalBalanceCents: number;
  };
  usage24h: {
    count: number;
    costCents: number;
  };
  lowBalanceUsers: Array<{
    id: string;
    username: string;
    displayName: string;
    balanceCents: number;
  }>;
  recentUsage: UsageRecord[];
  recentLedger: LedgerEntry[];
  llm: {
    keyCount: number;
    selectedKeyName: string | null;
    globalModel: string | null;
  } | null;
};
