import type {
  Event,
  createOpencodeClient,
  Project,
  Model,
  Provider,
  Permission,
  UserMessage,
  Part,
  Auth,
  Config,
} from "@opencode-ai/sdk"

import type { BunShell } from "./shell"
import { type ToolDefinition } from "./tool"

export * from "./tool"

export type PluginInput = {
  client: ReturnType<typeof createOpencodeClient>
  project: Project
  directory: string
  worktree: string
  $: BunShell
}

export type Plugin = (input: PluginInput) => Promise<Hooks>

export interface Hooks {
  event?: (input: { event: Event }) => Promise<void>
  config?: (input: Config) => Promise<void>
  tool?: {
    [key: string]: ToolDefinition
  }
  auth?: {
    provider: string
    loader?: (auth: () => Promise<Auth>, provider: Provider) => Promise<Record<string, any>>
    methods: (
      | {
          type: "oauth"
          label: string
          prompts?: Array<
            | {
                type: "text"
                key: string
                message: string
                placeholder?: string
                validate?: (value: string) => string | undefined
                condition?: (inputs: Record<string, string>) => boolean
              }
            | {
                type: "select"
                key: string
                message: string
                options: Array<{
                  label: string
                  value: string
                  hint?: string
                }>
                condition?: (inputs: Record<string, string>) => boolean
              }
          >
          authorize(inputs?: Record<string, string>): Promise<
            { url: string; instructions: string } & (
              | {
                  method: "auto"
                  callback(): Promise<
                    | ({
                        type: "success"
                        provider?: string
                      } & (
                        | {
                            refresh: string
                            access: string
                            expires: number
                          }
                        | { key: string }
                      ))
                    | {
                        type: "failed"
                      }
                  >
                }
              | {
                  method: "code"
                  callback(code: string): Promise<
                    | ({
                        type: "success"
                        provider?: string
                      } & (
                        | {
                            refresh: string
                            access: string
                            expires: number
                          }
                        | { key: string }
                      ))
                    | {
                        type: "failed"
                      }
                  >
                }
            )
          >
        }
      | {
          type: "api"
          label: string
          prompts?: Array<
            | {
                type: "text"
                key: string
                message: string
                placeholder?: string
                validate?: (value: string) => string | undefined
                condition?: (inputs: Record<string, string>) => boolean
              }
            | {
                type: "select"
                key: string
                message: string
                options: Array<{
                  label: string
                  value: string
                  hint?: string
                }>
                condition?: (inputs: Record<string, string>) => boolean
              }
          >
          authorize?(inputs?: Record<string, string>): Promise<
            | {
                type: "success"
                key: string
                provider?: string
              }
            | {
                type: "failed"
              }
          >
        }
    )[]
  }
  /**
   * Called when a new message is received
   */
  "chat.message"?: (
    input: { sessionID: string; agent?: string; model?: { providerID: string; modelID: string }; messageID?: string },
    output: { message: UserMessage; parts: Part[] },
  ) => Promise<void>
  /**
   * Modify parameters sent to LLM
   */
  "chat.params"?: (
    input: { sessionID: string; agent: string; model: Model; provider: Provider; message: UserMessage },
    output: { temperature: number; topP: number; options: Record<string, any> },
  ) => Promise<void>
  "permission.ask"?: (input: Permission, output: { status: "ask" | "deny" | "allow" }) => Promise<void>
  "tool.execute.before"?: (
    input: { tool: string; sessionID: string; callID: string },
    output: { args: any },
  ) => Promise<void>
  "tool.execute.after"?: (
    input: { tool: string; sessionID: string; callID: string },
    output: {
      title: string
      output: string
      metadata: any
    },
  ) => Promise<void>
}
