import type { ClaudeSessionSummary, ClaudeSessionDetail, ClaudeChatMessage } from './claude-sessions';
import { ensureServer } from './opencode/server-manager';
import { getConnection, officerMeta } from './opencode/client';
import { logger } from './logger';

// The OpenCode analog of claude-sessions.ts. OpenCode's own store is the source of truth, read via the
// fixed pm2-managed server's HTTP API (never the DB directly). All OpenCode /chat sessions live in that
// one server's project, so listing is just GET /session. Returns the same shapes as the Claude reader,
// tagged harness:'opencode', so chat.ts can merge both harnesses transparently.

/**
 * List OpenCode sessions tagged with the given logical cwd (via `metadata.officer.cwd`), so each
 * context (/chat pwd, email account, project) sees only its own. With no cwd, returns all. Never
 * throws — returns [] if the server is unavailable.
 */
export async function listOpenCodeSessions(cwd?: string): Promise<ClaudeSessionSummary[]> {
  try {
    const { baseUrl } = await ensureServer();
    const sessions = await getConnection(baseUrl).listSessions();
    return sessions
      .filter((s) => !cwd || officerMeta(s.metadata).cwd === cwd)
      .map((s) => {
        const created = s.time?.created ?? Date.now();
        const updated = s.time?.updated ?? created;
        return {
          id: s.id,
          title: s.title || '(untitled)',
          cwd: s.location?.directory ?? '',
          createdAt: new Date(created).toISOString(),
          updatedAt: new Date(updated).toISOString(),
          messageCount: 0, // the session list endpoint doesn't include a turn count
          harness: 'opencode',
        } satisfies ClaudeSessionSummary;
      });
  } catch (err) {
    logger.warn('Failed to list OpenCode sessions', { error: String(err) });
    return [];
  }
}

/** Load one OpenCode session's transcript, rebuilt into the shared display message shape. */
export async function loadOpenCodeSession(sessionId: string): Promise<ClaudeSessionDetail | null> {
  try {
    const { baseUrl } = await ensureServer();
    const stored = await getConnection(baseUrl).getMessages(sessionId);

    const messages: ClaudeChatMessage[] = [];
    let modelId = '';

    for (const m of stored) {
      const role = m.info?.role;
      const parts = m.parts ?? [];

      if (role === 'user') {
        const text = parts
          .filter((p) => p.type === 'text')
          .map((p) => p.text ?? '')
          .join('')
          .trim();
        if (text) messages.push({ role: 'user', text });
        continue;
      }

      if (role === 'assistant') {
        if (!modelId) modelId = m.info?.model?.modelID ?? m.info?.modelID ?? '';
        for (const p of parts) {
          if (p.type === 'text' && p.text) {
            messages.push({ role: 'assistant', id: `${sessionId}-${messages.length}`, text: p.text });
          } else if (p.type === 'tool' && p.callID) {
            messages.push({
              role: 'tool',
              toolName: p.tool ?? 'tool',
              toolInput: p.state?.input ?? {},
              toolCallId: p.callID,
              output: p.state?.output,
              isError: Boolean(p.state?.error),
            });
          }
          // skip reasoning/step-* parts (parity with the streaming delta filter).
        }
      }
    }

    return { id: sessionId, model: modelId ? `opencode/${modelId}` : 'opencode', cwd: '', messages };
  } catch (err) {
    logger.warn('Failed to load OpenCode session', { sessionId, error: String(err) });
    return null;
  }
}

export async function deleteOpenCodeSession(sessionId: string): Promise<boolean> {
  try {
    const { baseUrl } = await ensureServer();
    return await getConnection(baseUrl).deleteSession(sessionId);
  } catch (err) {
    logger.warn('Failed to delete OpenCode session', { sessionId, error: String(err) });
    return false;
  }
}

export async function renameOpenCodeSession(sessionId: string, title: string): Promise<boolean> {
  try {
    const { baseUrl } = await ensureServer();
    return await getConnection(baseUrl).renameSession(sessionId, title);
  } catch (err) {
    logger.warn('Failed to rename OpenCode session', { sessionId, error: String(err) });
    return false;
  }
}

/** OpenCode session ids are prefixed `ses_`; Claude ids are bare UUIDs. */
export const isOpenCodeSessionId = (id: string): boolean => id.startsWith('ses_');
