import { Effect, Option } from "effect"
import { Argument, Command, Flag } from "effect/unstable/cli"
import type { TestActions } from "../services/TestActions.ts"
import { logAction } from "../services/TestActions.ts"

// Deeply nested admin commands
const usersList = Command.make("list", {
  // Optional option with default
  format: Flag.string("format").pipe(
    Flag.withDescription("Output format (json, table, csv)"),
    Flag.withDefault("table")
  ),
  // Boolean flag
  active: Flag.boolean("active").pipe(
    Flag.withDescription("Show only active users")
  ),
  // Option with both short and long aliases
  verbose: Flag.boolean("verbose").pipe(
    Flag.withAlias("v"),
    Flag.withDescription("Show detailed information")
  )
}, (config) =>
  logAction("users list", {
    format: config.format,
    active: config.active,
    verbose: config.verbose
  })).pipe(
    Command.withDescription("List all users in the system")
  )

const usersCreate = Command.make("create", {
  // Required positional argument
  username: Argument.string("username").pipe(
    Argument.withDescription("Username for the new user")
  ),
  // Optional positional argument
  email: Argument.string("email").pipe(
    Argument.withDescription("Email address (optional)"),
    Argument.optional
  ),
  // Required option
  role: Flag.string("role").pipe(
    Flag.withDescription("User role (admin, user, guest)")
  ),
  // Boolean with explicit value support
  notify: Flag.boolean("notify").pipe(
    Flag.withAlias("n"),
    Flag.withDescription("Send notification email")
  )
}, (config) =>
  logAction("users create", {
    username: config.username,
    email: config.email,
    role: config.role,
    notify: config.notify
  })).pipe(
    Command.withDescription("Create a new user account")
  )

const users = Command.make("users").pipe(
  Command.withDescription("User management commands"),
  Command.withSubcommands([usersList, usersCreate])
)

const configSet = Command.make("set", {
  // Variadic positional arguments
  pairs: Argument.string("key=value").pipe(
    Argument.withDescription("Configuration key-value pairs"),
    Argument.variadic({ min: 1 })
  ),
  // File path option
  file: Flag.file("config-file").pipe(
    Flag.withAlias("f"),
    Flag.withDescription("Write to specific config file"),
    Flag.optional
  )
}, (config) =>
  logAction("config set", {
    pairs: config.pairs,
    file: config.file
  })).pipe(
    Command.withDescription("Set configuration values")
  )

const configGet = Command.make("get", {
  // Single required positional
  key: Argument.string("key").pipe(
    Argument.withDescription("Configuration key to retrieve")
  ),
  // Options with different types
  source: Flag.string("source").pipe(
    Flag.withDescription("Configuration source (local, global, system)"),
    Flag.withDefault("local")
  )
}, (config) =>
  logAction("config get", {
    key: config.key,
    source: config.source
  })).pipe(
    Command.withDescription("Get configuration value")
  )

const config = Command.make("config").pipe(
  Command.withSharedFlags({
    // Parent command options shared with config subcommands
    profile: Flag.string("profile").pipe(
      Flag.withAlias("p"),
      Flag.withDescription("Configuration profile to use"),
      Flag.optional
    )
  }),
  Command.withDescription("Manage application configuration"),
  Command.withSubcommands([configSet, configGet])
)

const admin = Command.make("admin").pipe(
  Command.withSharedFlags({
    // Boolean that can be set to false explicitly
    sudo: Flag.boolean("sudo").pipe(
      Flag.withDescription("Run with elevated privileges")
    )
  }),
  Command.withDescription("Administrative commands"),
  Command.withSubcommands([users, config])
)

// File operations commands
const copy = Command.make("copy", {
  // Multiple required positional arguments (do not require actual filesystem presence in tests)
  source: Argument.file("source", { mustExist: false }).pipe(
    Argument.withDescription("Source file or directory")
  ),
  destination: Argument.file("destination", { mustExist: false }).pipe(
    Argument.withDescription("Destination path")
  ),
  // Boolean flags with short aliases
  recursive: Flag.boolean("recursive").pipe(
    Flag.withAlias("r"),
    Flag.withDescription("Copy directories recursively")
  ),
  force: Flag.boolean("force").pipe(
    Flag.withAlias("f"),
    Flag.withDescription("Overwrite existing files")
  ),
  // Integer option
  buffer: Flag.integer("buffer-size").pipe(
    Flag.withDescription("Buffer size in KB"),
    Flag.withDefault(64)
  )
}, (config) =>
  logAction("copy", {
    source: config.source,
    destination: config.destination,
    recursive: config.recursive,
    force: config.force,
    bufferSize: config.buffer
  })).pipe(
    Command.withDescription("Copy files or directories")
  )

const move = Command.make("move", {
  // Variadic sources with at least 2 items
  paths: Argument.string("paths").pipe(
    Argument.withDescription("Source path(s) and destination"),
    Argument.variadic({ min: 2 })
  ),
  // Options
  interactive: Flag.boolean("interactive").pipe(
    Flag.withAlias("i"),
    Flag.withDescription("Prompt before overwrite")
  )
}, (config) =>
  logAction("move", {
    paths: config.paths,
    interactive: config.interactive
  })).pipe(
    Command.withDescription("Move or rename files")
  )

const remove = Command.make("remove", {
  // Variadic with no upper limit
  files: Argument.string("files").pipe(
    Argument.withDescription("Files to remove"),
    Argument.variadic({ min: 1 })
  ),
  // Multiple boolean options
  recursive: Flag.boolean("recursive").pipe(
    Flag.withAlias("r"),
    Flag.withDescription("Remove directories and contents")
  ),
  force: Flag.boolean("force").pipe(
    Flag.withAlias("f"),
    Flag.withDescription("Force removal without prompts")
  ),
  verbose: Flag.boolean("verbose").pipe(
    Flag.withAlias("v"),
    Flag.withDescription("Explain what is being done")
  )
}, (config) =>
  logAction("remove", {
    files: config.files,
    recursive: config.recursive,
    force: config.force,
    verbose: config.verbose
  })).pipe(
    Command.withDescription("Remove files or directories")
  )

// Build command for testing option aliases
const build = Command.make("build", {
  output: Flag.string("output").pipe(
    Flag.withAlias("o"),
    Flag.withDescription("Output directory")
  ),
  verbose: Flag.boolean("verbose").pipe(
    Flag.withAlias("v"),
    Flag.withDescription("Enable verbose output")
  ),
  configFile: Flag.string("config-file").pipe(
    Flag.withAlias("f"),
    Flag.withDescription("Configuration file path"),
    Flag.optional
  )
}, (config) =>
  logAction("build", {
    output: config.output,
    verbose: config.verbose,
    config: Option.getOrElse(config.configFile, () => "none")
  })).pipe(
    Command.withDescription("Build the project")
  )

// Git-style commands for testing subcommands and context sharing
const gitClone = Command.make("clone", {
  repository: Argument.string("repository").pipe(
    Argument.withDescription("Repository URL or path")
  ),
  branch: Flag.string("branch").pipe(
    Flag.withDefault("main"),
    Flag.withDescription("Branch to clone")
  )
}, (config) =>
  logAction("git clone", {
    repository: config.repository,
    branch: config.branch
  })).pipe(
    Command.withDescription("Clone a repository")
  )

const gitAdd = Command.make("add", {
  files: Argument.string("files").pipe(
    Argument.withDescription("Files to add")
  ),
  update: Flag.boolean("update").pipe(
    Flag.withDescription("Update tracked files")
  )
}, (config) =>
  logAction("git add", {
    files: config.files,
    update: config.update
  })).pipe(
    Command.withDescription("Add files to staging")
  )

const gitStatus = Command.make("status", {
  short: Flag.boolean("short").pipe(
    Flag.withDescription("Show short format")
  )
}, (config) =>
  logAction("git status", {
    short: config.short
  })).pipe(
    Command.withDescription("Show repository status")
  )

const git = Command.make("git").pipe(
  Command.withSharedFlags({
    verbose: Flag.boolean("verbose").pipe(
      Flag.withDescription("Enable verbose output")
    )
  }),
  Command.withHandler((config) =>
    logAction("git", {
      verbose: config.verbose
    })
  ),
  Command.withDescription("Git version control"),
  Command.withSubcommands([gitClone, gitAdd, gitStatus])
)

// Commands for testing error handling
const testRequired = Command.make("test-required", {
  required: Flag.string("required").pipe(
    Flag.withDescription("A required option for testing")
  )
}, (config) =>
  logAction("test-required", {
    required: config.required
  })).pipe(
    Command.withDescription("Test command with required option")
  )

const testFailing: Command.Command<
  "test-failing",
  { readonly input: string },
  {},
  string,
  TestActions
> = Command.make("test-failing", {
  input: Flag.string("input").pipe(
    Flag.withDescription("Input that will cause handler to fail")
  )
}, (config) =>
  Effect.gen(function*() {
    yield* logAction("test-failing", { input: config.input })
    return yield* Effect.fail("Handler error")
  })).pipe(
    Command.withDescription("Test command that always fails")
  )

// Deploy command for testing complex nested structures
const deployCommand = Command.make("deploy", {
  service: Argument.string("service").pipe(
    Argument.withDescription("Service to deploy")
  ),
  environment: Argument.string("environment").pipe(
    Argument.withDescription("Target environment")
  ),
  database: {
    host: Flag.string("db-host").pipe(
      Flag.withDescription("Database host")
    ),
    port: Flag.integer("db-port").pipe(
      Flag.withDescription("Database port")
    )
  },
  dryRun: Flag.boolean("dry-run").pipe(
    Flag.withDescription("Perform a dry run")
  )
}, (config) =>
  logAction("deploy", {
    service: config.service,
    environment: config.environment,
    database: config.database,
    dryRun: config.dryRun
  })).pipe(
    Command.withDescription("Deploy a service")
  )

const app = Command.make("app").pipe(
  Command.withSharedFlags({
    env: Flag.string("env").pipe(
      Flag.withDescription("Environment setting"),
      Flag.optional
    )
  }),
  Command.withHandler((config) =>
    logAction("app", {
      env: config.env
    })
  ),
  Command.withDescription("Application management"),
  Command.withSubcommands([deployCommand])
)

// Service command for nested context sharing tests
const serviceCommand = Command.make("service").pipe(
  Command.withSharedFlags({
    name: Flag.string("name").pipe(
      Flag.withDescription("Service name")
    )
  }),
  Command.withHandler((config) =>
    logAction("service", {
      name: config.name
    })
  ),
  Command.withDescription("Service management"),
  Command.withSubcommands([deployCommand])
)

const appWithService = Command.make("app-nested").pipe(
  Command.withSharedFlags({
    env: Flag.string("env").pipe(
      Flag.withDescription("Environment setting")
    )
  }),
  Command.withHandler((config) =>
    logAction("app-nested", {
      env: config.env
    })
  ),
  Command.withDescription("Application with nested services"),
  Command.withSubcommands([serviceCommand])
)

// Main command with global options
// Note: No handler on root command - running with no args should show help
export const ComprehensiveCli = Command.make("mycli").pipe(
  Command.withSharedFlags({
    // Global options available to all subcommands
    debug: Flag.boolean("debug").pipe(
      Flag.withAlias("d"),
      Flag.withDescription("Enable debug logging")
    ),
    config: Flag.file("config").pipe(
      Flag.withAlias("c"),
      Flag.withDescription("Path to configuration file"),
      Flag.optional
    ),
    quiet: Flag.boolean("quiet").pipe(
      Flag.withAlias("q"),
      Flag.withDescription("Suppress non-error output")
    )
  }),
  Command.withDescription("A comprehensive CLI tool demonstrating all features"),
  Command.withSubcommands([admin, copy, move, remove, build, git, testRequired, testFailing, app, appWithService])
)

export const run = Command.runWith(ComprehensiveCli, {
  version: "1.0.0"
})
