import { intro, outro } from '@clack/prompts';
import chalk from 'chalk';
import { command } from 'cleye';
import * as dotenv from 'dotenv';
import { existsSync, readFileSync, writeFileSync } from 'fs';
import { parse as iniParse, stringify as iniStringify } from 'ini';
import { homedir } from 'os';
import { join as pathJoin, resolve as pathResolve } from 'path';
import { COMMANDS } from './ENUMS';
import { TEST_MOCK_TYPES } from '../engine/testAi';
import { getI18nLocal, i18n } from '../i18n';

export enum CONFIG_KEYS {
  OCO_API_KEY = 'OCO_API_KEY',
  OCO_TOKENS_MAX_INPUT = 'OCO_TOKENS_MAX_INPUT',
  OCO_TOKENS_MAX_OUTPUT = 'OCO_TOKENS_MAX_OUTPUT',
  OCO_DESCRIPTION = 'OCO_DESCRIPTION',
  OCO_EMOJI = 'OCO_EMOJI',
  OCO_MODEL = 'OCO_MODEL',
  OCO_LANGUAGE = 'OCO_LANGUAGE',
  OCO_WHY = 'OCO_WHY',
  OCO_MESSAGE_TEMPLATE_PLACEHOLDER = 'OCO_MESSAGE_TEMPLATE_PLACEHOLDER',
  OCO_PROMPT_MODULE = 'OCO_PROMPT_MODULE',
  OCO_AI_PROVIDER = 'OCO_AI_PROVIDER',
  OCO_ONE_LINE_COMMIT = 'OCO_ONE_LINE_COMMIT',
  OCO_TEST_MOCK_TYPE = 'OCO_TEST_MOCK_TYPE',
  OCO_API_URL = 'OCO_API_URL',
  OCO_PROXY = 'OCO_PROXY',
  OCO_API_CUSTOM_HEADERS = 'OCO_API_CUSTOM_HEADERS',
  OCO_OMIT_SCOPE = 'OCO_OMIT_SCOPE',
  OCO_GITPUSH = 'OCO_GITPUSH', // todo: deprecate
  OCO_HOOK_AUTO_UNCOMMENT = 'OCO_HOOK_AUTO_UNCOMMENT',
  OCO_OLLAMA_THINK = 'OCO_OLLAMA_THINK'
}

export enum CONFIG_MODES {
  get = 'get',
  set = 'set',
  describe = 'describe'
}

export const MODEL_LIST = {
  openai: [
    'gpt-4o-mini',
    'gpt-3.5-turbo',
    'gpt-3.5-turbo-instruct',
    'gpt-3.5-turbo-0613',
    'gpt-3.5-turbo-0301',
    'gpt-3.5-turbo-1106',
    'gpt-3.5-turbo-0125',
    'gpt-3.5-turbo-16k',
    'gpt-3.5-turbo-16k-0613',
    'gpt-3.5-turbo-16k-0301',
    'gpt-4',
    'gpt-4-0314',
    'gpt-4-0613',
    'gpt-4-1106-preview',
    'gpt-4-0125-preview',
    'gpt-4-turbo-preview',
    'gpt-4-vision-preview',
    'gpt-4-1106-vision-preview',
    'gpt-4-turbo',
    'gpt-4-turbo-2024-04-09',
    'gpt-4-32k',
    'gpt-4-32k-0314',
    'gpt-4-32k-0613',
    'gpt-4o',
    'gpt-4o-2024-05-13',
    'gpt-4o-mini-2024-07-18'
  ],

  anthropic: [
    'claude-sonnet-4-20250514',
    'claude-opus-4-20250514',
    'claude-3-7-sonnet-20250219',
    'claude-3-5-sonnet-20241022',
    'claude-3-5-haiku-20241022'
  ],

  gemini: [
    'gemini-1.5-flash',
    'gemini-1.5-pro',
    'gemini-1.0-pro',
    'gemini-pro-vision',
    'text-embedding-004'
  ],

  groq: [
    'llama3-70b-8192', // Meta Llama 3 70B (default one, no daily token limit and 14 400 reqs/day)
    'llama3-8b-8192', // Meta Llama 3 8B
    'llama-guard-3-8b', // Llama Guard 3 8B
    'llama-3.1-8b-instant', // Llama 3.1 8B (Preview)
    'llama-3.1-70b-versatile', // Llama 3.1 70B (Preview)
    'gemma-7b-it', // Gemma 7B
    'gemma2-9b-it' // Gemma 2 9B
  ],

  mistral: [
    'ministral-3b-2410',
    'ministral-3b-latest',
    'ministral-8b-2410',
    'ministral-8b-latest',
    'open-mistral-7b',
    'mistral-tiny',
    'mistral-tiny-2312',
    'open-mistral-nemo',
    'open-mistral-nemo-2407',
    'mistral-tiny-2407',
    'mistral-tiny-latest',
    'open-mixtral-8x7b',
    'mistral-small',
    'mistral-small-2312',
    'open-mixtral-8x22b',
    'open-mixtral-8x22b-2404',
    'mistral-small-2402',
    'mistral-small-2409',
    'mistral-small-latest',
    'mistral-medium-2312',
    'mistral-medium',
    'mistral-medium-latest',
    'mistral-large-2402',
    'mistral-large-2407',
    'mistral-large-2411',
    'mistral-large-latest',
    'pixtral-large-2411',
    'pixtral-large-latest',
    'codestral-2405',
    'codestral-latest',
    'codestral-mamba-2407',
    'open-codestral-mamba',
    'codestral-mamba-latest',
    'pixtral-12b-2409',
    'pixtral-12b',
    'pixtral-12b-latest',
    'mistral-embed',
    'mistral-moderation-2411',
    'mistral-moderation-latest'
  ],
  deepseek: ['deepseek-chat', 'deepseek-reasoner'],

  // AI/ML API available chat-completion models
  // https://api.aimlapi.com/v1/models
  aimlapi: [
    'openai/gpt-4o',
    'gpt-4o-2024-08-06',
    'gpt-4o-2024-05-13',
    'gpt-4o-mini',
    'gpt-4o-mini-2024-07-18',
    'chatgpt-4o-latest',
    'gpt-4-turbo',
    'gpt-4-turbo-2024-04-09',
    'gpt-4',
    'gpt-4-0125-preview',
    'gpt-4-1106-preview',
    'gpt-3.5-turbo',
    'gpt-3.5-turbo-0125',
    'gpt-3.5-turbo-1106',
    'o1-preview',
    'o1-preview-2024-09-12',
    'o1-mini',
    'o1-mini-2024-09-12',
    'o3-mini',
    'gpt-4o-audio-preview',
    'gpt-4o-mini-audio-preview',
    'gpt-4o-search-preview',
    'gpt-4o-mini-search-preview',
    'openai/gpt-4.1-2025-04-14',
    'openai/gpt-4.1-mini-2025-04-14',
    'openai/gpt-4.1-nano-2025-04-14',
    'openai/o4-mini-2025-04-16',
    'openai/o3-2025-04-16',
    'o1',
    'openai/o3-pro',
    'meta-llama/Llama-3.2-90B-Vision-Instruct-Turbo',
    'google/gemma-2-27b-it',
    'meta-llama/Llama-Vision-Free',
    'Qwen/Qwen2-72B-Instruct',
    'mistralai/Mixtral-8x7B-Instruct-v0.1',
    'nvidia/Llama-3.1-Nemotron-70B-Instruct-HF',
    'NousResearch/Nous-Hermes-2-Mixtral-8x7B-DPO',
    'meta-llama/Llama-3.3-70B-Instruct-Turbo',
    'meta-llama/Llama-3.2-3B-Instruct-Turbo',
    'meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo',
    'meta-llama/Llama-Guard-3-11B-Vision-Turbo',
    'Qwen/Qwen2.5-7B-Instruct-Turbo',
    'Qwen/Qwen2.5-Coder-32B-Instruct',
    'meta-llama/Meta-Llama-3-8B-Instruct-Lite',
    'meta-llama/Llama-3-8b-chat-hf',
    'meta-llama/Llama-3-70b-chat-hf',
    'Qwen/Qwen2.5-72B-Instruct-Turbo',
    'Qwen/QwQ-32B',
    'meta-llama/Meta-Llama-3.1-405B-Instruct-Turbo',
    'meta-llama/Meta-Llama-3.1-8B-Instruct-Turbo',
    'meta-llama/Meta-Llama-3.1-70B-Instruct-Turbo',
    'mistralai/Mistral-7B-Instruct-v0.2',
    'meta-llama/LlamaGuard-2-8b',
    'mistralai/Mistral-7B-Instruct-v0.1',
    'mistralai/Mistral-7B-Instruct-v0.3',
    'meta-llama/Meta-Llama-Guard-3-8B',
    'meta-llama/llama-4-scout',
    'meta-llama/llama-4-maverick',
    'Qwen/Qwen3-235B-A22B-fp8-tput',
    'claude-3-opus-20240229',
    'claude-3-haiku-20240307',
    'claude-3-5-sonnet-20240620',
    'claude-3-5-sonnet-20241022',
    'claude-3-5-haiku-20241022',
    'claude-3-7-sonnet-20250219',
    'claude-sonnet-4-20250514',
    'claude-opus-4-20250514',
    'google/gemini-2.0-flash-exp',
    'google/gemini-2.0-flash',
    'google/gemini-2.5-pro',
    'google/gemini-2.5-flash',
    'deepseek-chat',
    'deepseek-reasoner',
    'qwen-max',
    'qwen-plus',
    'qwen-turbo',
    'qwen-max-2025-01-25',
    'mistralai/mistral-tiny',
    'mistralai/mistral-nemo',
    'anthracite-org/magnum-v4-72b',
    'nvidia/llama-3.1-nemotron-70b-instruct',
    'cohere/command-r-plus',
    'mistralai/codestral-2501',
    'google/gemma-3-4b-it',
    'google/gemma-3-12b-it',
    'google/gemma-3-27b-it',
    'google/gemini-2.5-flash-lite-preview',
    'deepseek/deepseek-prover-v2',
    'google/gemma-3n-e4b-it',
    'cohere/command-a',
    'MiniMax-Text-01',
    'abab6.5s-chat',
    'minimax/m1',
    'bagoodex/bagoodex-search-v1',
    'moonshot/kimi-k2-preview',
    'perplexity/sonar',
    'perplexity/sonar-pro',
    'x-ai/grok-4-07-09',
    'x-ai/grok-3-beta',
    'x-ai/grok-3-mini-beta'
  ],

  // OpenRouter available models
  // input_modalities: 'text'
  // output_modalities: 'text'
  // https://openrouter.ai/api/v1/models
  openrouter: [
    'openai/gpt-4o-mini', // used by default
    '01-ai/yi-large',
    'aetherwiing/mn-starcannon-12b',
    'agentica-org/deepcoder-14b-preview:free',
    'ai21/jamba-1.6-large',
    'ai21/jamba-1.6-mini',
    'aion-labs/aion-1.0',
    'aion-labs/aion-1.0-mini',
    'aion-labs/aion-rp-llama-3.1-8b',
    'alfredpros/codellama-7b-instruct-solidity',
    'all-hands/openhands-lm-32b-v0.1',
    'alpindale/goliath-120b',
    'alpindale/magnum-72b',
    'amazon/nova-lite-v1',
    'amazon/nova-micro-v1',
    'amazon/nova-pro-v1',
    'anthracite-org/magnum-v2-72b',
    'anthracite-org/magnum-v4-72b',
    'anthropic/claude-2',
    'anthropic/claude-2.0',
    'anthropic/claude-2.0:beta',
    'anthropic/claude-2.1',
    'anthropic/claude-2.1:beta',
    'anthropic/claude-2:beta',
    'anthropic/claude-3-haiku',
    'anthropic/claude-3-haiku:beta',
    'anthropic/claude-3-opus',
    'anthropic/claude-3-opus:beta',
    'anthropic/claude-3-sonnet',
    'anthropic/claude-3-sonnet:beta',
    'anthropic/claude-3.5-haiku',
    'anthropic/claude-3.5-haiku-20241022',
    'anthropic/claude-3.5-haiku-20241022:beta',
    'anthropic/claude-3.5-haiku:beta',
    'anthropic/claude-3.5-sonnet',
    'anthropic/claude-3.5-sonnet-20240620',
    'anthropic/claude-3.5-sonnet-20240620:beta',
    'anthropic/claude-3.5-sonnet:beta',
    'anthropic/claude-3.7-sonnet',
    'anthropic/claude-3.7-sonnet:beta',
    'anthropic/claude-3.7-sonnet:thinking',
    'anthropic/claude-opus-4',
    'anthropic/claude-sonnet-4',
    'arcee-ai/arcee-blitz',
    'arcee-ai/caller-large',
    'arcee-ai/coder-large',
    'arcee-ai/maestro-reasoning',
    'arcee-ai/spotlight',
    'arcee-ai/virtuoso-large',
    'arcee-ai/virtuoso-medium-v2',
    'arliai/qwq-32b-arliai-rpr-v1:free',
    'cognitivecomputations/dolphin-mixtral-8x22b',
    'cognitivecomputations/dolphin3.0-mistral-24b:free',
    'cognitivecomputations/dolphin3.0-r1-mistral-24b:free',
    'cohere/command',
    'cohere/command-a',
    'cohere/command-r',
    'cohere/command-r-03-2024',
    'cohere/command-r-08-2024',
    'cohere/command-r-plus',
    'cohere/command-r-plus-04-2024',
    'cohere/command-r-plus-08-2024',
    'cohere/command-r7b-12-2024',
    'deepseek/deepseek-chat',
    'deepseek/deepseek-chat-v3-0324',
    'deepseek/deepseek-chat-v3-0324:free',
    'deepseek/deepseek-chat:free',
    'deepseek/deepseek-prover-v2',
    'deepseek/deepseek-prover-v2:free',
    'deepseek/deepseek-r1',
    'deepseek/deepseek-r1-0528',
    'deepseek/deepseek-r1-0528-qwen3-8b',
    'deepseek/deepseek-r1-0528-qwen3-8b:free',
    'deepseek/deepseek-r1-0528:free',
    'deepseek/deepseek-r1-distill-llama-70b',
    'deepseek/deepseek-r1-distill-llama-70b:free',
    'deepseek/deepseek-r1-distill-llama-8b',
    'deepseek/deepseek-r1-distill-qwen-1.5b',
    'deepseek/deepseek-r1-distill-qwen-14b',
    'deepseek/deepseek-r1-distill-qwen-14b:free',
    'deepseek/deepseek-r1-distill-qwen-32b',
    'deepseek/deepseek-r1-distill-qwen-32b:free',
    'deepseek/deepseek-r1-distill-qwen-7b',
    'deepseek/deepseek-r1-zero:free',
    'deepseek/deepseek-r1:free',
    'deepseek/deepseek-v3-base:free',
    'eleutherai/llemma_7b',
    'eva-unit-01/eva-llama-3.33-70b',
    'eva-unit-01/eva-qwen-2.5-32b',
    'eva-unit-01/eva-qwen-2.5-72b',
    'featherless/qwerky-72b:free',
    'google/gemini-2.0-flash-001',
    'google/gemini-2.0-flash-exp:free',
    'google/gemini-2.0-flash-lite-001',
    'google/gemini-2.5-flash-preview',
    'google/gemini-2.5-flash-preview-05-20',
    'google/gemini-2.5-flash-preview-05-20:thinking',
    'google/gemini-2.5-flash-preview:thinking',
    'google/gemini-2.5-pro-exp-03-25',
    'google/gemini-2.5-pro-preview',
    'google/gemini-2.5-pro-preview-05-06',
    'google/gemini-flash-1.5',
    'google/gemini-flash-1.5-8b',
    'google/gemini-pro-1.5',
    'google/gemma-2-27b-it',
    'google/gemma-2-9b-it',
    'google/gemma-2-9b-it:free',
    'google/gemma-3-12b-it',
    'google/gemma-3-12b-it:free',
    'google/gemma-3-1b-it:free',
    'google/gemma-3-27b-it',
    'google/gemma-3-27b-it:free',
    'google/gemma-3-4b-it',
    'google/gemma-3-4b-it:free',
    'google/gemma-3n-e4b-it:free',
    'gryphe/mythomax-l2-13b',
    'inception/mercury-coder-small-beta',
    'infermatic/mn-inferor-12b',
    'inflection/inflection-3-pi',
    'inflection/inflection-3-productivity',
    'liquid/lfm-3b',
    'liquid/lfm-40b',
    'liquid/lfm-7b',
    'mancer/weaver',
    'meta-llama/llama-2-70b-chat',
    'meta-llama/llama-3-70b-instruct',
    'meta-llama/llama-3-8b-instruct',
    'meta-llama/llama-3.1-405b',
    'meta-llama/llama-3.1-405b-instruct',
    'meta-llama/llama-3.1-405b:free',
    'meta-llama/llama-3.1-70b-instruct',
    'meta-llama/llama-3.1-8b-instruct',
    'meta-llama/llama-3.1-8b-instruct:free',
    'meta-llama/llama-3.2-11b-vision-instruct',
    'meta-llama/llama-3.2-11b-vision-instruct:free',
    'meta-llama/llama-3.2-1b-instruct',
    'meta-llama/llama-3.2-1b-instruct:free',
    'meta-llama/llama-3.2-3b-instruct',
    'meta-llama/llama-3.2-3b-instruct:free',
    'meta-llama/llama-3.2-90b-vision-instruct',
    'meta-llama/llama-3.3-70b-instruct',
    'meta-llama/llama-3.3-70b-instruct:free',
    'meta-llama/llama-3.3-8b-instruct:free',
    'meta-llama/llama-4-maverick',
    'meta-llama/llama-4-maverick:free',
    'meta-llama/llama-4-scout',
    'meta-llama/llama-4-scout:free',
    'meta-llama/llama-guard-2-8b',
    'meta-llama/llama-guard-3-8b',
    'meta-llama/llama-guard-4-12b',
    'microsoft/mai-ds-r1:free',
    'microsoft/phi-3-medium-128k-instruct',
    'microsoft/phi-3-mini-128k-instruct',
    'microsoft/phi-3.5-mini-128k-instruct',
    'microsoft/phi-4',
    'microsoft/phi-4-multimodal-instruct',
    'microsoft/phi-4-reasoning-plus',
    'microsoft/phi-4-reasoning-plus:free',
    'microsoft/phi-4-reasoning:free',
    'microsoft/wizardlm-2-8x22b',
    'minimax/minimax-01',
    'mistralai/codestral-2501',
    'mistralai/devstral-small',
    'mistralai/devstral-small:free',
    'mistralai/magistral-medium-2506',
    'mistralai/magistral-medium-2506:thinking',
    'mistralai/magistral-small-2506',
    'mistralai/ministral-3b',
    'mistralai/ministral-8b',
    'mistralai/mistral-7b-instruct',
    'mistralai/mistral-7b-instruct-v0.1',
    'mistralai/mistral-7b-instruct-v0.2',
    'mistralai/mistral-7b-instruct-v0.3',
    'mistralai/mistral-7b-instruct:free',
    'mistralai/mistral-large',
    'mistralai/mistral-large-2407',
    'mistralai/mistral-large-2411',
    'mistralai/mistral-medium',
    'mistralai/mistral-medium-3',
    'mistralai/mistral-nemo',
    'mistralai/mistral-nemo:free',
    'mistralai/mistral-saba',
    'mistralai/mistral-small',
    'mistralai/mistral-small-24b-instruct-2501',
    'mistralai/mistral-small-24b-instruct-2501:free',
    'mistralai/mistral-small-3.1-24b-instruct',
    'mistralai/mistral-small-3.1-24b-instruct:free',
    'mistralai/mistral-tiny',
    'mistralai/mixtral-8x22b-instruct',
    'mistralai/mixtral-8x7b-instruct',
    'mistralai/pixtral-12b',
    'mistralai/pixtral-large-2411',
    'moonshotai/kimi-vl-a3b-thinking:free',
    'moonshotai/moonlight-16b-a3b-instruct:free',
    'neversleep/llama-3-lumimaid-70b',
    'neversleep/llama-3-lumimaid-8b',
    'neversleep/llama-3.1-lumimaid-70b',
    'neversleep/llama-3.1-lumimaid-8b',
    'neversleep/noromaid-20b',
    'nothingiisreal/mn-celeste-12b',
    'nousresearch/deephermes-3-llama-3-8b-preview:free',
    'nousresearch/deephermes-3-mistral-24b-preview:free',
    'nousresearch/hermes-2-pro-llama-3-8b',
    'nousresearch/hermes-3-llama-3.1-405b',
    'nousresearch/hermes-3-llama-3.1-70b',
    'nousresearch/nous-hermes-2-mixtral-8x7b-dpo',
    'nvidia/llama-3.1-nemotron-70b-instruct',
    'nvidia/llama-3.1-nemotron-ultra-253b-v1',
    'nvidia/llama-3.1-nemotron-ultra-253b-v1:free',
    'nvidia/llama-3.3-nemotron-super-49b-v1',
    'nvidia/llama-3.3-nemotron-super-49b-v1:free',
    'open-r1/olympiccoder-32b:free',
    'openai/chatgpt-4o-latest',
    'openai/codex-mini',
    'openai/gpt-3.5-turbo',
    'openai/gpt-3.5-turbo-0125',
    'openai/gpt-3.5-turbo-0613',
    'openai/gpt-3.5-turbo-1106',
    'openai/gpt-3.5-turbo-16k',
    'openai/gpt-3.5-turbo-instruct',
    'openai/gpt-4',
    'openai/gpt-4-0314',
    'openai/gpt-4-1106-preview',
    'openai/gpt-4-turbo',
    'openai/gpt-4-turbo-preview',
    'openai/gpt-4.1',
    'openai/gpt-4.1-mini',
    'openai/gpt-4.1-nano',
    'openai/gpt-4.5-preview',
    'openai/gpt-4o',
    'openai/gpt-4o-2024-05-13',
    'openai/gpt-4o-2024-08-06',
    'openai/gpt-4o-2024-11-20',
    'openai/gpt-4o-mini-2024-07-18',
    'openai/gpt-4o-mini-search-preview',
    'openai/gpt-4o-search-preview',
    'openai/gpt-4o:extended',
    'openai/o1',
    'openai/o1-mini',
    'openai/o1-mini-2024-09-12',
    'openai/o1-preview',
    'openai/o1-preview-2024-09-12',
    'openai/o1-pro',
    'openai/o3',
    'openai/o3-mini',
    'openai/o3-mini-high',
    'openai/o3-pro',
    'openai/o4-mini',
    'openai/o4-mini-high',
    'opengvlab/internvl3-14b:free',
    'opengvlab/internvl3-2b:free',
    'openrouter/auto',
    'perplexity/llama-3.1-sonar-large-128k-online',
    'perplexity/llama-3.1-sonar-small-128k-online',
    'perplexity/r1-1776',
    'perplexity/sonar',
    'perplexity/sonar-deep-research',
    'perplexity/sonar-pro',
    'perplexity/sonar-reasoning',
    'perplexity/sonar-reasoning-pro',
    'pygmalionai/mythalion-13b',
    'qwen/qwen-2-72b-instruct',
    'qwen/qwen-2.5-72b-instruct',
    'qwen/qwen-2.5-72b-instruct:free',
    'qwen/qwen-2.5-7b-instruct',
    'qwen/qwen-2.5-7b-instruct:free',
    'qwen/qwen-2.5-coder-32b-instruct',
    'qwen/qwen-2.5-coder-32b-instruct:free',
    'qwen/qwen-2.5-vl-7b-instruct',
    'qwen/qwen-2.5-vl-7b-instruct:free',
    'qwen/qwen-max',
    'qwen/qwen-plus',
    'qwen/qwen-turbo',
    'qwen/qwen-vl-max',
    'qwen/qwen-vl-plus',
    'qwen/qwen2.5-vl-32b-instruct',
    'qwen/qwen2.5-vl-32b-instruct:free',
    'qwen/qwen2.5-vl-3b-instruct:free',
    'qwen/qwen2.5-vl-72b-instruct',
    'qwen/qwen2.5-vl-72b-instruct:free',
    'qwen/qwen3-14b',
    'qwen/qwen3-14b:free',
    'qwen/qwen3-235b-a22b',
    'qwen/qwen3-235b-a22b:free',
    'qwen/qwen3-30b-a3b',
    'qwen/qwen3-30b-a3b:free',
    'qwen/qwen3-32b',
    'qwen/qwen3-32b:free',
    'qwen/qwen3-8b',
    'qwen/qwen3-8b:free',
    'qwen/qwq-32b',
    'qwen/qwq-32b-preview',
    'qwen/qwq-32b:free',
    'raifle/sorcererlm-8x22b',
    'rekaai/reka-flash-3:free',
    'sao10k/fimbulvetr-11b-v2',
    'sao10k/l3-euryale-70b',
    'sao10k/l3-lunaris-8b',
    'sao10k/l3.1-euryale-70b',
    'sao10k/l3.3-euryale-70b',
    'sarvamai/sarvam-m:free',
    'scb10x/llama3.1-typhoon2-70b-instruct',
    'sentientagi/dobby-mini-unhinged-plus-llama-3.1-8b',
    'shisa-ai/shisa-v2-llama3.3-70b:free',
    'sophosympatheia/midnight-rose-70b',
    'thedrummer/anubis-pro-105b-v1',
    'thedrummer/rocinante-12b',
    'thedrummer/skyfall-36b-v2',
    'thedrummer/unslopnemo-12b',
    'thedrummer/valkyrie-49b-v1',
    'thudm/glm-4-32b',
    'thudm/glm-4-32b:free',
    'thudm/glm-z1-32b',
    'thudm/glm-z1-32b:free',
    'thudm/glm-z1-rumination-32b',
    'tngtech/deepseek-r1t-chimera:free',
    'undi95/remm-slerp-l2-13b',
    'undi95/toppy-m-7b',
    'x-ai/grok-2-1212',
    'x-ai/grok-2-vision-1212',
    'x-ai/grok-3-beta',
    'x-ai/grok-3-mini-beta',
    'x-ai/grok-beta',
    'x-ai/grok-vision-beta'
  ]
};

const getDefaultModel = (provider: string | undefined): string => {
  switch (provider) {
    case 'ollama':
      return '';
    case 'mlx':
      return '';
    case 'anthropic':
      return MODEL_LIST.anthropic[0];
    case 'gemini':
      return MODEL_LIST.gemini[0];
    case 'groq':
      return MODEL_LIST.groq[0];
    case 'mistral':
      return MODEL_LIST.mistral[0];
    case 'deepseek':
      return MODEL_LIST.deepseek[0];
    case 'aimlapi':
      return MODEL_LIST.aimlapi[0];
    case 'openrouter':
      return MODEL_LIST.openrouter[0];
    default:
      return MODEL_LIST.openai[0];
  }
};

export enum DEFAULT_TOKEN_LIMITS {
  DEFAULT_MAX_TOKENS_INPUT = 4096,
  DEFAULT_MAX_TOKENS_OUTPUT = 500
}

const validateConfig = (
  key: string,
  condition: any,
  validationMessage: string
) => {
  if (!condition) {
    outro(`${chalk.red('✖')} wrong value for ${key}: ${validationMessage}.`);

    outro(
      'For more help refer to docs https://github.com/di-sukharev/opencommit'
    );

    process.exit(1);
  }
};

export const configValidators = {
  [CONFIG_KEYS.OCO_API_KEY](value: any, config: any = {}) {
    if (config.OCO_AI_PROVIDER !== 'openai') return value;

    validateConfig(
      'OCO_API_KEY',
      typeof value === 'string' && value.length > 0,
      'Empty value is not allowed'
    );

    validateConfig(
      'OCO_API_KEY',
      value,
      'You need to provide the OCO_API_KEY when OCO_AI_PROVIDER set to "openai" (default) or "ollama" or "mlx" or "azure" or "gemini" or "flowise" or "anthropic" or "deepseek". Run `oco config set OCO_API_KEY=your_key OCO_AI_PROVIDER=openai`'
    );

    return value;
  },

  [CONFIG_KEYS.OCO_DESCRIPTION](value: any) {
    validateConfig(
      CONFIG_KEYS.OCO_DESCRIPTION,
      typeof value === 'boolean',
      'Must be boolean: true or false'
    );

    return value;
  },

  [CONFIG_KEYS.OCO_API_CUSTOM_HEADERS](value) {
    try {
      // Custom headers must be a valid JSON string
      if (typeof value === 'string') {
        JSON.parse(value);
      }
      return value;
    } catch (error) {
      validateConfig(
        CONFIG_KEYS.OCO_API_CUSTOM_HEADERS,
        false,
        'Must be a valid JSON string of headers'
      );
    }
  },

  [CONFIG_KEYS.OCO_TOKENS_MAX_INPUT](value: any) {
    value = parseInt(value);
    validateConfig(
      CONFIG_KEYS.OCO_TOKENS_MAX_INPUT,
      !isNaN(value),
      'Must be a number'
    );

    return value;
  },

  [CONFIG_KEYS.OCO_TOKENS_MAX_OUTPUT](value: any) {
    value = parseInt(value);
    validateConfig(
      CONFIG_KEYS.OCO_TOKENS_MAX_OUTPUT,
      !isNaN(value),
      'Must be a number'
    );

    return value;
  },

  [CONFIG_KEYS.OCO_EMOJI](value: any) {
    validateConfig(
      CONFIG_KEYS.OCO_EMOJI,
      typeof value === 'boolean',
      'Must be boolean: true or false'
    );

    return value;
  },

  [CONFIG_KEYS.OCO_OMIT_SCOPE](value: any) {
    validateConfig(
      CONFIG_KEYS.OCO_OMIT_SCOPE,
      typeof value === 'boolean',
      'Must be boolean: true or false'
    );

    return value;
  },

  [CONFIG_KEYS.OCO_LANGUAGE](value: any) {
    const supportedLanguages = Object.keys(i18n);

    validateConfig(
      CONFIG_KEYS.OCO_LANGUAGE,
      getI18nLocal(value),
      `${value} is not supported yet. Supported languages: ${supportedLanguages}`
    );

    return getI18nLocal(value);
  },

  [CONFIG_KEYS.OCO_API_URL](value: any) {
    validateConfig(
      CONFIG_KEYS.OCO_API_URL,
      typeof value === 'string' && /^(https?:\/\/)/.test(value),
      `${value} is not a valid URL. It should start with 'http://' or 'https://'.`
    );
    return value;
  },

  [CONFIG_KEYS.OCO_PROXY](value: any) {
    validateConfig(
      CONFIG_KEYS.OCO_PROXY,
      value === null ||
        (typeof value === 'string' && /^(https?:\/\/)/.test(value)),
      `${value} is not a valid URL. It should start with 'http://' or 'https://'.`
    );
    return value;
  },

  [CONFIG_KEYS.OCO_MODEL](value: any, config: any = {}) {
    validateConfig(
      CONFIG_KEYS.OCO_MODEL,
      typeof value === 'string',
      `${value} is not supported yet, use:\n\n ${[
        ...MODEL_LIST.openai,
        ...MODEL_LIST.anthropic,
        ...MODEL_LIST.gemini
      ].join('\n')}`
    );
    return value;
  },

  [CONFIG_KEYS.OCO_MESSAGE_TEMPLATE_PLACEHOLDER](value: any) {
    validateConfig(
      CONFIG_KEYS.OCO_MESSAGE_TEMPLATE_PLACEHOLDER,
      value.startsWith('$'),
      `${value} must start with $, for example: '$msg'`
    );
    return value;
  },

  [CONFIG_KEYS.OCO_PROMPT_MODULE](value: any) {
    validateConfig(
      CONFIG_KEYS.OCO_PROMPT_MODULE,
      ['conventional-commit', '@commitlint'].includes(value),
      `${value} is not supported yet, use '@commitlint' or 'conventional-commit' (default)`
    );
    return value;
  },

  // todo: deprecate
  [CONFIG_KEYS.OCO_GITPUSH](value: any) {
    validateConfig(
      CONFIG_KEYS.OCO_GITPUSH,
      typeof value === 'boolean',
      'Must be true or false'
    );
    return value;
  },

  [CONFIG_KEYS.OCO_AI_PROVIDER](value: any) {
    if (!value) value = 'openai';

    validateConfig(
      CONFIG_KEYS.OCO_AI_PROVIDER,
      [
        'openai',
        'mistral',
        'anthropic',
        'gemini',
        'azure',
        'test',
        'flowise',
        'groq',
        'deepseek',
        'aimlapi',
        'openrouter'
      ].includes(value) || value.startsWith('ollama'),
      `${value} is not supported yet, use 'ollama', 'mlx', 'anthropic', 'azure', 'gemini', 'flowise', 'mistral', 'deepseek', 'aimlapi' or 'openai' (default)`
    );

    return value;
  },

  [CONFIG_KEYS.OCO_ONE_LINE_COMMIT](value: any) {
    validateConfig(
      CONFIG_KEYS.OCO_ONE_LINE_COMMIT,
      typeof value === 'boolean',
      'Must be true or false'
    );

    return value;
  },

  [CONFIG_KEYS.OCO_TEST_MOCK_TYPE](value: any) {
    validateConfig(
      CONFIG_KEYS.OCO_TEST_MOCK_TYPE,
      TEST_MOCK_TYPES.includes(value),
      `${value} is not supported yet, use ${TEST_MOCK_TYPES.map(
        (t) => `'${t}'`
      ).join(', ')}`
    );
    return value;
  },

  [CONFIG_KEYS.OCO_WHY](value: any) {
    validateConfig(
      CONFIG_KEYS.OCO_WHY,
      typeof value === 'boolean',
      'Must be true or false'
    );
    return value;
  },

  [CONFIG_KEYS.OCO_HOOK_AUTO_UNCOMMENT](value: any) {
    validateConfig(
      CONFIG_KEYS.OCO_HOOK_AUTO_UNCOMMENT,
      typeof value === 'boolean',
      'Must be true or false'
    );
  },

  [CONFIG_KEYS.OCO_OLLAMA_THINK](value: any) {
    validateConfig(
      CONFIG_KEYS.OCO_OLLAMA_THINK,
      typeof value === 'boolean',
      'Must be true or false'
    );
    return value;
  }
};

export enum OCO_AI_PROVIDER_ENUM {
  OLLAMA = 'ollama',
  OPENAI = 'openai',
  ANTHROPIC = 'anthropic',
  GEMINI = 'gemini',
  AZURE = 'azure',
  TEST = 'test',
  FLOWISE = 'flowise',
  GROQ = 'groq',
  MISTRAL = 'mistral',
  MLX = 'mlx',
  DEEPSEEK = 'deepseek',
  AIMLAPI = 'aimlapi',
  OPENROUTER = 'openrouter'
}

export const PROVIDER_API_KEY_URLS: Record<string, string | null> = {
  [OCO_AI_PROVIDER_ENUM.OPENAI]: 'https://platform.openai.com/api-keys',
  [OCO_AI_PROVIDER_ENUM.ANTHROPIC]:
    'https://console.anthropic.com/settings/keys',
  [OCO_AI_PROVIDER_ENUM.GEMINI]: 'https://aistudio.google.com/app/apikey',
  [OCO_AI_PROVIDER_ENUM.GROQ]: 'https://console.groq.com/keys',
  [OCO_AI_PROVIDER_ENUM.MISTRAL]: 'https://console.mistral.ai/api-keys/',
  [OCO_AI_PROVIDER_ENUM.DEEPSEEK]: 'https://platform.deepseek.com/api_keys',
  [OCO_AI_PROVIDER_ENUM.OPENROUTER]: 'https://openrouter.ai/keys',
  [OCO_AI_PROVIDER_ENUM.AIMLAPI]: 'https://aimlapi.com/app/keys',
  [OCO_AI_PROVIDER_ENUM.AZURE]: 'https://portal.azure.com/',
  [OCO_AI_PROVIDER_ENUM.OLLAMA]: null,
  [OCO_AI_PROVIDER_ENUM.MLX]: null,
  [OCO_AI_PROVIDER_ENUM.FLOWISE]: null,
  [OCO_AI_PROVIDER_ENUM.TEST]: null
};

export const RECOMMENDED_MODELS: Record<string, string> = {
  [OCO_AI_PROVIDER_ENUM.OPENAI]: 'gpt-4o-mini',
  [OCO_AI_PROVIDER_ENUM.ANTHROPIC]: 'claude-sonnet-4-20250514',
  [OCO_AI_PROVIDER_ENUM.GEMINI]: 'gemini-1.5-flash',
  [OCO_AI_PROVIDER_ENUM.GROQ]: 'llama3-70b-8192',
  [OCO_AI_PROVIDER_ENUM.MISTRAL]: 'mistral-small-latest',
  [OCO_AI_PROVIDER_ENUM.DEEPSEEK]: 'deepseek-chat',
  [OCO_AI_PROVIDER_ENUM.OPENROUTER]: 'openai/gpt-4o-mini',
  [OCO_AI_PROVIDER_ENUM.AIMLAPI]: 'gpt-4o-mini'
};

export type ConfigType = {
  [CONFIG_KEYS.OCO_API_KEY]?: string;
  [CONFIG_KEYS.OCO_TOKENS_MAX_INPUT]: number;
  [CONFIG_KEYS.OCO_TOKENS_MAX_OUTPUT]: number;
  [CONFIG_KEYS.OCO_API_URL]?: string;
  [CONFIG_KEYS.OCO_PROXY]?: string | null;
  [CONFIG_KEYS.OCO_API_CUSTOM_HEADERS]?: string;
  [CONFIG_KEYS.OCO_DESCRIPTION]: boolean;
  [CONFIG_KEYS.OCO_EMOJI]: boolean;
  [CONFIG_KEYS.OCO_WHY]: boolean;
  [CONFIG_KEYS.OCO_MODEL]: string;
  [CONFIG_KEYS.OCO_LANGUAGE]: string;
  [CONFIG_KEYS.OCO_MESSAGE_TEMPLATE_PLACEHOLDER]: string;
  [CONFIG_KEYS.OCO_PROMPT_MODULE]: OCO_PROMPT_MODULE_ENUM;
  [CONFIG_KEYS.OCO_AI_PROVIDER]: OCO_AI_PROVIDER_ENUM;
  [CONFIG_KEYS.OCO_GITPUSH]: boolean;
  [CONFIG_KEYS.OCO_ONE_LINE_COMMIT]: boolean;
  [CONFIG_KEYS.OCO_OMIT_SCOPE]: boolean;
  [CONFIG_KEYS.OCO_TEST_MOCK_TYPE]: string;
  [CONFIG_KEYS.OCO_HOOK_AUTO_UNCOMMENT]: boolean;
  [CONFIG_KEYS.OCO_OLLAMA_THINK]?: boolean;
};

export const defaultConfigPath = pathJoin(homedir(), '.opencommit');
export const defaultEnvPath = pathResolve(process.cwd(), '.env');

const assertConfigsAreValid = (config: Record<string, any>) => {
  for (const [key, value] of Object.entries(config)) {
    if (!value) continue;

    if (typeof value === 'string' && ['null', 'undefined'].includes(value)) {
      config[key] = undefined;
      continue;
    }

    try {
      const validate = configValidators[key as CONFIG_KEYS];
      validate(value, config);
    } catch (error) {
      outro(`Unknown '${key}' config option or missing validator.`);
      outro(
        `Manually fix the '.env' file or global '~/.opencommit' config file.`
      );

      process.exit(1);
    }
  }
};

enum OCO_PROMPT_MODULE_ENUM {
  CONVENTIONAL_COMMIT = 'conventional-commit',
  COMMITLINT = '@commitlint'
}

export const DEFAULT_CONFIG = {
  OCO_TOKENS_MAX_INPUT: DEFAULT_TOKEN_LIMITS.DEFAULT_MAX_TOKENS_INPUT,
  OCO_TOKENS_MAX_OUTPUT: DEFAULT_TOKEN_LIMITS.DEFAULT_MAX_TOKENS_OUTPUT,
  OCO_DESCRIPTION: false,
  OCO_EMOJI: false,
  OCO_MODEL: getDefaultModel('openai'),
  OCO_LANGUAGE: 'en',
  OCO_MESSAGE_TEMPLATE_PLACEHOLDER: '$msg',
  OCO_PROMPT_MODULE: OCO_PROMPT_MODULE_ENUM.CONVENTIONAL_COMMIT,
  OCO_AI_PROVIDER: OCO_AI_PROVIDER_ENUM.OPENAI,
  OCO_ONE_LINE_COMMIT: false,
  OCO_TEST_MOCK_TYPE: 'commit-message',
  OCO_WHY: false,
  OCO_OMIT_SCOPE: false,
  OCO_GITPUSH: true, // todo: deprecate
  OCO_HOOK_AUTO_UNCOMMENT: false
};

const initGlobalConfig = (configPath: string = defaultConfigPath) => {
  writeFileSync(configPath, iniStringify(DEFAULT_CONFIG), 'utf8');
  return DEFAULT_CONFIG;
};

const parseConfigVarValue = (value?: any) => {
  try {
    return JSON.parse(value);
  } catch (error) {
    return value;
  }
};

const getEnvConfig = (envPath: string) => {
  dotenv.config({ path: envPath });

  return {
    OCO_MODEL: process.env.OCO_MODEL,
    OCO_API_URL: process.env.OCO_API_URL,
    OCO_PROXY: process.env.OCO_PROXY,
    OCO_API_KEY: process.env.OCO_API_KEY,
    OCO_API_CUSTOM_HEADERS: process.env.OCO_API_CUSTOM_HEADERS,
    OCO_AI_PROVIDER: process.env.OCO_AI_PROVIDER as OCO_AI_PROVIDER_ENUM,

    OCO_TOKENS_MAX_INPUT: parseConfigVarValue(process.env.OCO_TOKENS_MAX_INPUT),
    OCO_TOKENS_MAX_OUTPUT: parseConfigVarValue(
      process.env.OCO_TOKENS_MAX_OUTPUT
    ),

    OCO_DESCRIPTION: parseConfigVarValue(process.env.OCO_DESCRIPTION),
    OCO_EMOJI: parseConfigVarValue(process.env.OCO_EMOJI),
    OCO_LANGUAGE: process.env.OCO_LANGUAGE,
    OCO_MESSAGE_TEMPLATE_PLACEHOLDER:
      process.env.OCO_MESSAGE_TEMPLATE_PLACEHOLDER,
    OCO_PROMPT_MODULE: process.env.OCO_PROMPT_MODULE as OCO_PROMPT_MODULE_ENUM,
    OCO_ONE_LINE_COMMIT: parseConfigVarValue(process.env.OCO_ONE_LINE_COMMIT),
    OCO_TEST_MOCK_TYPE: process.env.OCO_TEST_MOCK_TYPE,
    OCO_OMIT_SCOPE: parseConfigVarValue(process.env.OCO_OMIT_SCOPE),

    OCO_GITPUSH: parseConfigVarValue(process.env.OCO_GITPUSH) // todo: deprecate
  };
};

export const setGlobalConfig = (
  config: ConfigType,
  configPath: string = defaultConfigPath
) => {
  writeFileSync(configPath, iniStringify(config), 'utf8');
};

export const getIsGlobalConfigFileExist = (
  configPath: string = defaultConfigPath
) => {
  return existsSync(configPath);
};

export const getGlobalConfig = (configPath: string = defaultConfigPath) => {
  const isGlobalConfigFileExist = getIsGlobalConfigFileExist(configPath);
  if (!isGlobalConfigFileExist) {
    return { ...DEFAULT_CONFIG };
  }

  const configFile = readFileSync(configPath, 'utf8');
  return iniParse(configFile) as ConfigType;
};

/**
 * Merges two configs.
 * Env config takes precedence over global ~/.opencommit config file
 * @param main - env config
 * @param fallback - global ~/.opencommit config file
 * @returns merged config
 */
const mergeConfigs = (main: Partial<ConfigType>, fallback: ConfigType) => {
  const allKeys = new Set([...Object.keys(main), ...Object.keys(fallback)]);
  return Array.from(allKeys).reduce((acc, key) => {
    const mainValue = main[key];
    acc[key] = parseConfigVarValue(
      mainValue !== undefined ? mainValue : fallback[key]
    );
    return acc;
  }, {} as ConfigType);
};

interface GetConfigOptions {
  globalPath?: string;
  envPath?: string;
  setDefaultValues?: boolean;
}

const cleanUndefinedValues = (config: ConfigType) => {
  return Object.fromEntries(
    Object.entries(config).map(([_, v]) => {
      try {
        if (typeof v === 'string') {
          if (v === 'undefined') return [_, undefined];
          if (v === 'null') return [_, null];

          const parsedValue = JSON.parse(v);
          return [_, parsedValue];
        }
        return [_, v];
      } catch (error) {
        return [_, v];
      }
    })
  );
};

export const getConfig = ({
  envPath = defaultEnvPath,
  globalPath = defaultConfigPath
}: GetConfigOptions = {}): ConfigType => {
  const envConfig = getEnvConfig(envPath);
  const globalConfig = getGlobalConfig(globalPath);

  const config = mergeConfigs(envConfig, globalConfig);

  const cleanConfig = cleanUndefinedValues(config);

  return cleanConfig as ConfigType;
};

export const setConfig = (
  keyValues: [key: string, value: string | boolean | number | null][],
  globalConfigPath: string = defaultConfigPath
) => {
  const config = getConfig({
    globalPath: globalConfigPath
  });

  const configToSet = {};

  for (let [key, value] of keyValues) {
    if (!configValidators.hasOwnProperty(key)) {
      const supportedKeys = Object.keys(configValidators).join('\n');
      throw new Error(
        `Unsupported config key: ${key}. Expected keys are:\n\n${supportedKeys}.\n\nFor more help refer to our docs: https://github.com/di-sukharev/opencommit`
      );
    }

    let parsedConfigValue;

    try {
      if (typeof value === 'string') parsedConfigValue = JSON.parse(value);
      else parsedConfigValue = value;
    } catch (error) {
      parsedConfigValue = value;
    }

    const validValue = configValidators[key as CONFIG_KEYS](
      parsedConfigValue,
      config
    );

    configToSet[key] = validValue;
  }

  setGlobalConfig(mergeConfigs(configToSet, config), globalConfigPath);

  outro(`${chalk.green('✔')} config successfully set`);
};

// --- HELP MESSAGE GENERATION ---
function getConfigKeyDetails(key) {
  switch (key) {
    case CONFIG_KEYS.OCO_MODEL:
      return {
        description: 'The AI model to use for generating commit messages',
        values: MODEL_LIST
      };
    case CONFIG_KEYS.OCO_AI_PROVIDER:
      return {
        description: 'The AI provider to use',
        values: Object.values(OCO_AI_PROVIDER_ENUM)
      };
    case CONFIG_KEYS.OCO_PROMPT_MODULE:
      return {
        description: 'The prompt module to use for commit message generation',
        values: Object.values(OCO_PROMPT_MODULE_ENUM)
      };
    case CONFIG_KEYS.OCO_LANGUAGE:
      return {
        description: 'The locale to use for commit messages',
        values: Object.keys(i18n)
      };
    case CONFIG_KEYS.OCO_TEST_MOCK_TYPE:
      return {
        description: 'The type of test mock to use',
        values: ['commit-message', 'prompt-module-commitlint-config']
      };
    case CONFIG_KEYS.OCO_ONE_LINE_COMMIT:
      return {
        description: 'One line commit message',
        values: ['true', 'false']
      };
    case CONFIG_KEYS.OCO_DESCRIPTION:
      return {
        description:
          'Postface a message with ~3 sentences description of the changes',
        values: ['true', 'false']
      };
    case CONFIG_KEYS.OCO_EMOJI:
      return {
        description: 'Preface a message with GitMoji',
        values: ['true', 'false']
      };
    case CONFIG_KEYS.OCO_WHY:
      return {
        description:
          'Output a short description of why the changes were done after the commit message (default: false)',
        values: ['true', 'false']
      };
    case CONFIG_KEYS.OCO_OMIT_SCOPE:
      return {
        description: 'Do not include a scope in the commit message',
        values: ['true', 'false']
      };
    case CONFIG_KEYS.OCO_GITPUSH:
      return {
        description:
          'Push to git after commit (deprecated). If false, oco will exit after committing',
        values: ['true', 'false']
      };
    case CONFIG_KEYS.OCO_TOKENS_MAX_INPUT:
      return {
        description: 'Max model token limit',
        values: ['Any positive integer']
      };
    case CONFIG_KEYS.OCO_TOKENS_MAX_OUTPUT:
      return {
        description: 'Max response tokens',
        values: ['Any positive integer']
      };
    case CONFIG_KEYS.OCO_API_KEY:
      return {
        description: 'API key for the selected provider',
        values: ['String (required for most providers)']
      };
    case CONFIG_KEYS.OCO_API_URL:
      return {
        description:
          'Custom API URL - may be used to set proxy path to OpenAI API',
        values: ["URL string (must start with 'http://' or 'https://')"]
      };
    case CONFIG_KEYS.OCO_PROXY:
      return {
        description: 'HTTP/HTTPS Proxy URL',
        values: [
          "URL string (must start with 'http://' or 'https://')",
          'null (disable proxy even when HTTP_PROXY/HTTPS_PROXY are set)'
        ]
      };
    case CONFIG_KEYS.OCO_MESSAGE_TEMPLATE_PLACEHOLDER:
      return {
        description: 'Message template placeholder',
        values: ['String (must start with $)']
      };
    case CONFIG_KEYS.OCO_HOOK_AUTO_UNCOMMENT:
      return {
        description: 'Automatically uncomment the commit message in the hook',
        values: ['true', 'false']
      };
    default:
      return {
        description: 'String value',
        values: ['Any string']
      };
  }
}

function printConfigKeyHelp(param) {
  if (!Object.values(CONFIG_KEYS).includes(param)) {
    console.log(chalk.red(`Unknown config parameter: ${param}`));
    return;
  }

  const details = getConfigKeyDetails(param as CONFIG_KEYS);

  let desc = details.description;
  let defaultValue = undefined;
  if (param in DEFAULT_CONFIG) {
    defaultValue = DEFAULT_CONFIG[param];
  }

  console.log(chalk.bold(`\n${param}:`));
  console.log(chalk.gray(`  Description: ${desc}`));
  if (defaultValue !== undefined) {
    // Print booleans and numbers as-is, strings without quotes
    if (typeof defaultValue === 'string') {
      console.log(chalk.gray(`  Default: ${defaultValue}`));
    } else {
      console.log(chalk.gray(`  Default: ${defaultValue}`));
    }
  }

  if (Array.isArray(details.values)) {
    console.log(chalk.gray('  Accepted values:'));
    details.values.forEach((value) => {
      console.log(chalk.gray(`    - ${value}`));
    });
  } else {
    console.log(chalk.gray('  Accepted values by provider:'));
    Object.entries(details.values).forEach(([provider, values]) => {
      console.log(chalk.gray(`    ${provider}:`));
      (values as string[]).forEach((value) => {
        console.log(chalk.gray(`      - ${value}`));
      });
    });
  }
}

function printAllConfigHelp() {
  console.log(chalk.bold('Available config parameters:'));
  for (const key of Object.values(CONFIG_KEYS).sort()) {
    const details = getConfigKeyDetails(key);
    // Try to get the default value from DEFAULT_CONFIG
    let defaultValue = undefined;
    if (key in DEFAULT_CONFIG) {
      defaultValue = DEFAULT_CONFIG[key];
    }

    console.log(chalk.bold(`\n${key}:`));
    console.log(chalk.gray(`  Description: ${details.description}`));
    if (defaultValue !== undefined) {
      if (typeof defaultValue === 'string') {
        console.log(chalk.gray(`  Default: ${defaultValue}`));
      } else {
        console.log(chalk.gray(`  Default: ${defaultValue}`));
      }
    }
  }
  console.log(
    chalk.yellow(
      '\nUse "oco config describe [PARAMETER]" to see accepted values and more details for a specific config parameter.'
    )
  );
}

export const configCommand = command(
  {
    name: COMMANDS.config,
    parameters: ['<mode>', '[key=values...]'],
    help: {
      description: 'Configure opencommit settings',
      examples: [
        'Describe all config parameters: oco config describe',
        'Describe a specific parameter: oco config describe OCO_MODEL',
        'Get a config value: oco config get OCO_MODEL',
        'Set a config value: oco config set OCO_MODEL=gpt-4'
      ]
    }
  },
  async (argv) => {
    try {
      const { mode, keyValues } = argv._;
      intro(`COMMAND: config ${mode} ${keyValues}`);

      if (mode === CONFIG_MODES.describe) {
        if (!keyValues || keyValues.length === 0) {
          printAllConfigHelp();
        } else {
          for (const key of keyValues) {
            printConfigKeyHelp(key);
          }
        }
        process.exit(0);
      } else if (mode === CONFIG_MODES.get) {
        if (!keyValues || keyValues.length === 0) {
          throw new Error('No config keys specified for get mode');
        }
        const config = getConfig() || {};
        for (const key of keyValues) {
          outro(`${key}=${config[key as keyof typeof config]}`);
        }
      } else if (mode === CONFIG_MODES.set) {
        if (!keyValues || keyValues.length === 0) {
          throw new Error('No config keys specified for set mode');
        }
        await setConfig(
          keyValues.map((keyValue) => keyValue.split('=') as [string, string])
        );
      } else {
        throw new Error(
          `Unsupported mode: ${mode}. Valid modes are: "set", "get", and "describe"`
        );
      }
    } catch (error) {
      outro(`${chalk.red('✖')} ${error}`);
      process.exit(1);
    }
  }
);
