import matter from 'gray-matter';

export type NoteVisibility = 'private' | 'org' | 'public';
export type NoteMode = 'search' | 'inject';

const VALID_VISIBILITY: ReadonlyArray<NoteVisibility> = ['private', 'org', 'public'];
const VALID_MODE: ReadonlyArray<NoteMode> = ['search', 'inject'];

export interface NoteFm {
  title: string | null;
  visibility: NoteVisibility;
  scope_org_id: string | null;
  mode_hint: NoteMode | null;
  tags: string[];
}

export interface ParsedNote {
  fm: NoteFm;
  body: string;
}

/**
 * Parse a Markdown string with optional YAML frontmatter.
 * Validates visibility, scope_org_id (required for org), and mode_hint.
 * Missing FM fields fall back to safe defaults.
 */
export function parseNoteFm(md: string): ParsedNote {
  const parsed = matter(md);
  const data = parsed.data ?? {};
  const body = parsed.content;

  // visibility
  const rawVis = data.visibility;
  let visibility: NoteVisibility;
  if (rawVis === undefined || rawVis === null) {
    visibility = 'private';
  } else if (VALID_VISIBILITY.includes(rawVis as NoteVisibility)) {
    visibility = rawVis as NoteVisibility;
  } else {
    throw new Error(`invalid visibility value: ${JSON.stringify(rawVis)} — must be one of ${VALID_VISIBILITY.join(', ')}`);
  }

  // scope_org_id
  const rawScopeOrgId = data.scope_org_id;
  let scope_org_id: string | null = null;
  if (typeof rawScopeOrgId === 'string' && rawScopeOrgId.trim() !== '') {
    scope_org_id = rawScopeOrgId.trim();
  } else if (rawScopeOrgId !== undefined && rawScopeOrgId !== null) {
    // provided but not a usable string — treat as absent
    scope_org_id = null;
  }

  if (visibility === 'org' && !scope_org_id) {
    throw new Error('scope_org_id is required when visibility is "org"');
  }

  // Only retain scope_org_id when visibility is 'org'; clear it otherwise
  scope_org_id = visibility === 'org' ? scope_org_id : null;

  // mode_hint
  const rawMode = data.mode_hint;
  let mode_hint: NoteMode | null = null;
  if (rawMode !== undefined && rawMode !== null) {
    if (VALID_MODE.includes(rawMode as NoteMode)) {
      mode_hint = rawMode as NoteMode;
    } else {
      throw new Error(`invalid mode_hint value: ${JSON.stringify(rawMode)} — must be one of ${VALID_MODE.join(', ')}`);
    }
  }

  // title
  const title = typeof data.title === 'string' && data.title.trim() !== ''
    ? data.title.trim()
    : null;

  // tags — filter to string-only elements
  const rawTags = data.tags;
  let tags: string[] = [];
  if (Array.isArray(rawTags)) {
    tags = rawTags.filter((t): t is string => typeof t === 'string');
  } else if (typeof rawTags === 'string' && rawTags.trim() !== '') {
    tags = [rawTags.trim()];
  }

  return {
    fm: { title, visibility, scope_org_id, mode_hint, tags },
    body,
  };
}

/**
 * Serialize a NoteFm + body back into a Markdown string with YAML frontmatter.
 * Null fields are omitted from the frontmatter.
 */
export function serializeNoteFm(fm: NoteFm, body: string): string {
  const data: Record<string, unknown> = {
    visibility: fm.visibility,
  };

  if (fm.title !== null) data.title = fm.title;
  if (fm.scope_org_id !== null) data.scope_org_id = fm.scope_org_id;
  if (fm.mode_hint !== null) data.mode_hint = fm.mode_hint;
  if (fm.tags.length > 0) data.tags = fm.tags;

  return matter.stringify(body, data);
}
