/**
 * Enum for all possible node types
 */
export enum NodeType {
  Root = "root",
  Section = "section",
  Text = "text",
  Template = "template",
  See = "see",
  Do = "do",
  Buy = "buy",
  Eat = "eat",
  Drink = "drink",
  Sleep = "sleep",
  Listing = "listing",
  Marker = "marker",
}

/**
 * Base interface for all node types in the Wikivoyage tree
 */
export interface BaseNode {
  /** Type identifier for the node */
  type: NodeType;
  /** Properties specific to this node type */
  properties: Record<string, any>;
  /** Child nodes */
  children: WikiNode[];
}

/**
 * Root node of the document
 */
export interface RootNode extends BaseNode {
  type: NodeType.Root;
  properties: {
    /** Page banner information */
    pagebanner?: {
      [key: string]: string;
    };
    /** Map frame information */
    mapframe?: {
      [key: string]: string;
    };
    /** Route box information */
    routebox?: {
      [key: string]: string;
    };
    /** Geographic coordinates */
    geo?: {
      [key: string]: string;
    };
    /** Parent region information */
    isPartOf?: {
      [key: string]: string;
    };
    /** City status information */
    usablecity?: {
      [key: string]: string;
    };
    guidecity?: {
      [key: string]: string;
    };
    outlinecity?: {
      [key: string]: string;
    };
    title: string;
  };
}

/**
 * Section node representing a heading and its content
 */
export interface SectionNode extends BaseNode {
  type: NodeType.Section;
  properties: {
    /** Section title */
    title: string;
    /** Heading level (1-6) */
    level: number;
  };
}

/**
 * Text node containing markdown content
 */
export interface TextNode extends BaseNode {
  type: NodeType.Text;
  properties: {
    /** Markdown formatted text */
    markdown: string;
  };
}

/**
 * Generic template node for templates not specifically handled
 */
export interface TemplateNode extends BaseNode {
  type: NodeType.Template;
  properties: {
    /** Template name */
    name: string;
    /** Template parameters */
    params: {
      [key: string]: string;
    };
  };
}

/**
 * Base interface for listing templates (see, do, buy, eat, drink, sleep)
 */
export interface ListingNode extends BaseNode {
  properties: {
    /** Name of the listing */
    name: string;
    /** Alternative name */
    alt?: string;
    /** URL for the listing */
    url?: string;
    /** Email address */
    email?: string;
    /** Physical address */
    address?: string;
    /** Latitude coordinate */
    lat?: string;
    /** Longitude coordinate */
    long?: string;
    /** Directions to reach the location */
    directions?: string;
    /** Phone number */
    phone?: string;
    /** Toll-free phone number */
    tollfree?: string;
    /** Fax number */
    fax?: string;
    /** Opening hours */
    hours?: string;
    /** Price information */
    price?: string;
    /** Last edit timestamp */
    lastedit?: string;
    /** Wikipedia article name */
    wikipedia?: string;
    /** Wikidata ID */
    wikidata?: string;
    /** Image filename */
    image?: string;
    /** Description content */
    content?: string;
  };
}

/**
 * See listing (attractions, landmarks)
 */
export interface SeeNode extends ListingNode {
  type: NodeType.See;
}

/**
 * Do listing (activities)
 */
export interface DoNode extends ListingNode {
  type: NodeType.Do;
}

/**
 * Buy listing (shopping)
 */
export interface BuyNode extends ListingNode {
  type: NodeType.Buy;
}

/**
 * Eat listing (restaurants)
 */
export interface EatNode extends ListingNode {
  type: NodeType.Eat;
}

/**
 * Drink listing (bars, cafes)
 */
export interface DrinkNode extends ListingNode {
  type: NodeType.Drink;
}

/**
 * Sleep listing (accommodations)
 */
export interface SleepNode extends ListingNode {
  type: NodeType.Sleep;
  properties: ListingNode["properties"] & {
    /** Check-in time */
    checkin?: string;
    /** Check-out time */
    checkout?: string;
  };
}

/**
 * Generic listing node
 */
export interface GenericListingNode extends ListingNode {
  type: NodeType.Listing;
}

/**
 * Marker node for map locations
 */
export interface MarkerNode extends BaseNode {
  type: NodeType.Marker;
  properties: {
    /** Type of marker */
    type: string;
    /** Name of the location */
    name: string;
    /** Latitude coordinate */
    lat: string;
    /** Longitude coordinate */
    long: string;
  };
}

/**
 * Union type of all possible node types
 */
export type WikiNode<T extends NodeType = NodeType> = T extends NodeType.Root
  ? RootNode
  : T extends NodeType.Section
    ? SectionNode
    : T extends NodeType.Text
      ? TextNode
      : T extends NodeType.Template
        ? TemplateNode
        : T extends NodeType.See
          ? SeeNode
          : T extends NodeType.Do
            ? DoNode
            : T extends NodeType.Buy
              ? BuyNode
              : T extends NodeType.Eat
                ? EatNode
                : T extends NodeType.Drink
                  ? DrinkNode
                  : T extends NodeType.Sleep
                    ? SleepNode
                    : T extends NodeType.Listing
                      ? GenericListingNode
                      : T extends NodeType.Marker
                        ? MarkerNode
                        : never;