import { readdir } from "node:fs/promises"
import { gpiod, cstr } from "./ffi"
import { Output } from "./output"
import { Input } from "./input"
import type * as Type from "./types"
import {
  GPIOError,
  PermissionError,
  PinInUseError,
  ChipNotFoundError,
  InvalidConfigError,
} from "./errors"

class GPIO {
  #chipPath: string

  constructor(options?: { chip?: string }) {
    this.#chipPath = options?.chip ?? "/dev/gpiochip0"
  }

  output(pin: number, options?: Type.OutputOptions): Output {
    return new Output(this.#chipPath, pin, options)
  }

  input(pin: number, options?: Type.InputOptions): Input {
    return new Input(this.#chipPath, pin, options)
  }

  async listChips(): Promise<Type.ChipInfo[]> {
    const chips: Type.ChipInfo[] = []

    try {
      const files = await readdir("/dev")
      const chipFiles = files.filter((f) => f.startsWith("gpiochip"))

      for (const file of chipFiles) {
        const path = `/dev/${file}`

        try {
          const chip = gpiod.gpiod_chip_open(cstr(path))
          if (!chip) continue

          const info = gpiod.gpiod_chip_get_info(chip)
          if (!info) {
            gpiod.gpiod_chip_close(chip)
            continue
          }

          const name = gpiod.gpiod_chip_info_get_name(info)
          const label = gpiod.gpiod_chip_info_get_label(info)
          const numLines = gpiod.gpiod_chip_info_get_num_lines(info)

          chips.push({
            path,
            name: String(name || ""),
            label: String(label || ""),
            numLines: Number(numLines),
          })

          gpiod.gpiod_chip_close(chip)
        } catch {
          continue
        }
      }
    } catch {
      // /dev might not be accessible, return empty array
    }

    return chips
  }

  static Error = GPIOError
  static PermissionError = PermissionError
  static PinInUseError = PinInUseError
  static ChipNotFoundError = ChipNotFoundError
  static InvalidConfigError = InvalidConfigError
}

namespace GPIO {
  export type PullMode = Type.PullMode
  export type EdgeMode = Type.EdgeMode
  export type InputOptions = Type.InputOptions
  export type OutputOptions = Type.OutputOptions
  export type InputEvent = Type.InputEvent
  export type Input = import("./input").Input
  export type Output = import("./output").Output
}

export default GPIO
