"""
Main CLI entry point for klug-secrets.
"""
import os
import sys
import click
import requests
import json
from typing import Optional, Dict, Any

# Default API endpoint (can be overridden with env var)
DEFAULT_API_ENDPOINT = os.environ.get('KLUG_AUTOCLOUD_API_ENDPOINT', 
                                      'https://i7u8rj2mtg.execute-api.eu-west-3.amazonaws.com/prod')

def get_api_key():
    """Get API key from environment, exit if not set."""
    api_key = os.environ.get('KLUG_AUTOCLOUD_API_KEY')
    if not api_key:
        click.echo(click.style('Error: KLUG_AUTOCLOUD_API_KEY environment variable not set', fg='red'), err=True)
        sys.exit(1)
    return api_key


def make_request(method: str, path: str, data: Optional[Dict] = None, params: Optional[Dict] = None) -> Dict[str, Any]:
    """Make an authenticated request to the API."""
    api_key = get_api_key()
    url = f"{DEFAULT_API_ENDPOINT}{path}"
    headers = {
        'Authorization': f'Bearer {api_key}',
        'Content-Type': 'application/json'
    }
    
    try:
        if method == 'GET':
            response = requests.get(url, headers=headers, params=params)
        elif method == 'POST':
            response = requests.post(url, headers=headers, json=data)
        elif method == 'PUT':
            response = requests.put(url, headers=headers, json=data)
        elif method == 'DELETE':
            response = requests.delete(url, headers=headers)
        else:
            raise ValueError(f"Unsupported method: {method}")
        
        response.raise_for_status()
        return response.json()
    except requests.exceptions.HTTPError as e:
        error_msg = "Unknown error"
        try:
            error_data = e.response.json()
            error_msg = error_data.get('error', str(e))
        except:
            error_msg = str(e)
        click.echo(click.style(f'Error: {error_msg}', fg='red'), err=True)
        sys.exit(1)
    except requests.exceptions.RequestException as e:
        click.echo(click.style(f'Request error: {str(e)}', fg='red'), err=True)
        sys.exit(1)


@click.group()
@click.version_option(version='1.0.0')
def cli():
    """KLUG Secrets - Manage secrets and environments via CLI."""
    pass


# Secrets commands
@cli.group()
def secrets():
    """Manage secrets."""
    pass


@secrets.command('list')
@click.option('--metadata', is_flag=True, help='Show full metadata (created_at, updated_at, etc.)')
def secrets_list(metadata: bool):
    """List all secrets for your account. By default, shows one secret name per line."""
    result = make_request('GET', '/secrets')
    secrets = result.get('secrets', [])
    
    if not secrets:
        if not metadata:
            # Don't output anything if no secrets and not showing metadata
            return
        click.echo('No secrets found.')
        return
    
    if metadata:
        # Show full metadata
        click.echo(f"Found {result.get('count', 0)} secret(s):\n")
        for secret in secrets:
            click.echo(f"  {click.style(secret.get('secret_name', secret['secret_id']), fg='cyan')}")
            if 'created_at' in secret:
                click.echo(f"    Created: {secret['created_at']}")
            if 'updated_at' in secret:
                click.echo(f"    Updated: {secret['updated_at']}")
            click.echo()
    else:
        # Default: one secret name per line
        for secret in secrets:
            secret_name = secret.get('secret_name', secret.get('secret_id'))
            click.echo(secret_name)


@secrets.command('get')
@click.argument('name')
@click.option('--metadata', is_flag=True, help='Show full metadata (created_at, updated_at, etc.)')
def secrets_get(name: str, metadata: bool):
    """Get a secret by name. By default, only returns the secret value."""
    params = {'include_value': 'true'}
    result = make_request('GET', f'/secrets/{name}', params=params)
    
    # If --metadata flag is set, show full information
    if metadata:
        click.echo(f"Secret: {click.style(name, fg='cyan')}")
        if 'created_at' in result:
            click.echo(f"Created: {result['created_at']}")
        if 'updated_at' in result:
            click.echo(f"Updated: {result['updated_at']}")
        if 'value' in result:
            click.echo(f"Value: {click.style(result['value'], fg='green')}")
    else:
        # Default: only output the value (no newline for easy piping)
        if 'value' in result:
            click.echo(result['value'], nl=False)


@secrets.command('add')
@click.argument('name')
@click.argument('value')
@click.option('--metadata', help='JSON metadata string')
def secrets_add(name: str, value: str, metadata: Optional[str]):
    """Add a new secret."""
    data = {
        'secret_id': name,
        'value': value
    }
    
    if metadata:
        try:
            data['metadata'] = json.loads(metadata)
        except json.JSONDecodeError:
            click.echo(click.style('Error: Invalid JSON in --metadata', fg='red'), err=True)
            sys.exit(1)
    
    result = make_request('POST', '/secrets', data=data)
    click.echo(click.style(f'✓ Secret "{name}" created successfully', fg='green'))


@secrets.command('update')
@click.argument('name')
@click.option('--value', help='New secret value')
@click.option('--metadata', help='JSON metadata string')
def secrets_update(name: str, value: Optional[str], metadata: Optional[str]):
    """Update an existing secret."""
    data = {}
    
    if value:
        data['value'] = value
    
    if metadata:
        try:
            data['metadata'] = json.loads(metadata)
        except json.JSONDecodeError:
            click.echo(click.style('Error: Invalid JSON in --metadata', fg='red'), err=True)
            sys.exit(1)
    
    if not data:
        click.echo(click.style('Error: Must provide --value or --metadata', fg='red'), err=True)
        sys.exit(1)
    
    result = make_request('PUT', f'/secrets/{name}', data=data)
    click.echo(click.style(f'✓ Secret "{name}" updated successfully', fg='green'))


@secrets.command('delete')
@click.argument('name')
@click.confirmation_option(prompt='Are you sure you want to delete this secret?')
def secrets_delete(name: str):
    """Delete a secret."""
    result = make_request('DELETE', f'/secrets/{name}')
    click.echo(click.style(f'✓ Secret "{name}" deleted successfully', fg='green'))


# Environments commands
@cli.group(invoke_without_command=True)
@click.option('--system', 'system_id', help='System ID (for stdin input)')
@click.option('--env', 'env_name', help='Environment name (for stdin input)')
@click.option('--file', 'file_path', type=click.Path(exists=True), help='Path to .env file (alternative to stdin)')
@click.pass_context
def envs(ctx, system_id: Optional[str], env_name: Optional[str], file_path: Optional[str]):
    """Manage environments.
    
    For stdin input: cat .env | klug-secrets envs --system main --env dev
    For subcommands: klug-secrets envs list, klug-secrets envs get --system X --env Y, etc.
    """
    # If invoked without subcommand and has --system/--env, treat as stdin input
    if ctx.invoked_subcommand is None:
        if system_id and env_name:
            # Handle stdin input
            if file_path:
                with open(file_path, 'r') as f:
                    file_contents = f.read()
            else:
                # Read from stdin
                if sys.stdin.isatty():
                    click.echo(click.style('Error: No input provided. Pipe a .env file or use --file option.', fg='red'), err=True)
                    sys.exit(1)
                file_contents = sys.stdin.read()
            
            if not file_contents.strip():
                click.echo(click.style('Error: No content provided', fg='red'), err=True)
                sys.exit(1)
            
            # Validate that the content is in VARNAME=varval format
            lines = file_contents.strip().split('\n')
            invalid_lines = []
            for i, line in enumerate(lines, 1):
                line = line.strip()
                # Skip empty lines and comments
                if not line or line.startswith('#'):
                    continue
                # Check if line matches VARNAME=varval format
                if '=' not in line:
                    invalid_lines.append((i, line))
                else:
                    # Check that there's at least one character before the first '='
                    key_part = line.split('=', 1)[0].strip()
                    if not key_part:
                        invalid_lines.append((i, line))
            
            if invalid_lines:
                click.echo(click.style('Error: Invalid .env file format. All lines must be in VARNAME=varval format.', fg='red'), err=True)
                click.echo(click.style('Invalid lines:', fg='red'), err=True)
                for line_num, line_content in invalid_lines:
                    click.echo(click.style(f'  Line {line_num}: {line_content}', fg='red'), err=True)
                sys.exit(1)
            
            data = {'fileContents': file_contents}
            result = make_request('POST', f'/envs/{system_id}/{env_name}', data=data)
            click.echo(click.style(f'✓ Environment "{system_id}/{env_name}" created successfully', fg='green'))
        else:
            # Show help if no subcommand and no required options
            click.echo(ctx.get_help())
            sys.exit(0)


@envs.command('list')
@click.option('--system', 'system_id', help='Filter by system ID')
@click.option('--metadata', is_flag=True, help='Show full metadata (created_at, updated_at, etc.)')
def envs_list(system_id: Optional[str], metadata: bool):
    """List all environments for your account. By default, shows one environment per line (system_id/env_name)."""
    params = {}
    if system_id:
        params['system_id'] = system_id
    
    result = make_request('GET', '/envs', params=params)
    environments = result.get('environments', [])
    
    if not environments:
        if not metadata:
            # Don't output anything if no environments and not showing metadata
            return
        click.echo('No environments found.')
        return
    
    if metadata:
        # Show full metadata
        click.echo(f"Found {result.get('count', 0)} environment(s):\n")
        for env in environments:
            click.echo(f"  {click.style(env['system_id'], fg='cyan')}/{click.style(env['env_name'], fg='yellow')}")
            if 'created_at' in env:
                click.echo(f"    Created: {env['created_at']}")
            if 'updated_at' in env:
                click.echo(f"    Updated: {env['updated_at']}")
            click.echo()
    else:
        # Default: one environment per line (system_id/env_name)
        for env in environments:
            click.echo(f"{env['system_id']}/{env['env_name']}")


@envs.command('get')
@click.option('--system', 'system_id', required=True, help='System ID')
@click.option('--env', 'env_name', required=True, help='Environment name')
@click.option('--metadata', is_flag=True, help='Show full metadata (created_at, updated_at, etc.)')
def envs_get(system_id: str, env_name: str, metadata: bool):
    """Get an environment. By default, only returns the environment variables in .env format."""
    result = make_request('GET', f'/envs/{system_id}/{env_name}')
    
    if 'env_vars' in result:
        env_vars = result['env_vars']
        
        if metadata:
            # Show full metadata
            click.echo(f"Environment: {click.style(system_id, fg='cyan')}/{click.style(env_name, fg='yellow')}")
            if 'created_at' in result:
                click.echo(f"Created: {result['created_at']}")
            if 'updated_at' in result:
                click.echo(f"Updated: {result['updated_at']}")
            click.echo("\nEnvironment variables:")
            for key, value in sorted(env_vars.items()):
                click.echo(f"  {click.style(key, fg='green')}={value}")
        else:
            # Default: output as .env file format (one KEY=VALUE per line)
            for key, value in sorted(env_vars.items()):
                # Escape special characters in value if needed
                if isinstance(value, str):
                    # If value contains newlines, spaces, or special chars, quote it
                    if '\n' in value or ' ' in value or '#' in value or '=' in value:
                        # Escape quotes and backslashes
                        escaped_value = value.replace('\\', '\\\\').replace('"', '\\"')
                        click.echo(f'{key}="{escaped_value}"')
                    else:
                        click.echo(f'{key}={value}')
                else:
                    click.echo(f'{key}={value}')


@envs.command('add')
@click.option('--system', 'system_id', required=True, help='System ID')
@click.option('--env', 'env_name', required=True, help='Environment name')
@click.option('--file', 'file_path', type=click.Path(exists=True), help='Path to .env file')
@click.option('--var', 'vars', multiple=True, help='Environment variable in format KEY=VALUE (can be used multiple times)')
def envs_add(system_id: str, env_name: str, file_path: Optional[str], vars: tuple):
    """Add a new environment."""
    env_vars = {}
    
    if file_path:
        # Read from file
        with open(file_path, 'r') as f:
            file_contents = f.read()
        # Parse .env file
        for line in file_contents.split('\n'):
            line = line.strip()
            if not line or line.startswith('#'):
                continue
            if '=' not in line:
                continue
            key, value = line.split('=', 1)
            key = key.strip()
            value = value.strip()
            # Remove quotes if present
            if (value.startswith('"') and value.endswith('"')) or \
               (value.startswith("'") and value.endswith("'")):
                value = value[1:-1]
            if key:
                env_vars[key] = value
    
    # Add vars from command line
    for var in vars:
        if '=' not in var:
            click.echo(click.style(f'Warning: Skipping invalid var format: {var}', fg='yellow'), err=True)
            continue
        key, value = var.split('=', 1)
        env_vars[key.strip()] = value.strip()
    
    if not env_vars:
        click.echo(click.style('Error: Must provide --file or --var', fg='red'), err=True)
        sys.exit(1)
    
    data = {'env_vars': env_vars}
    result = make_request('POST', f'/envs/{system_id}/{env_name}', data=data)
    click.echo(click.style(f'✓ Environment "{system_id}/{env_name}" created successfully', fg='green'))




@envs.command('update')
@click.option('--system', 'system_id', required=True, help='System ID')
@click.option('--env', 'env_name', required=True, help='Environment name')
@click.option('--file', 'file_path', type=click.Path(exists=True), help='Path to .env file (alternative to stdin)')
@click.option('--var', 'vars', multiple=True, help='Environment variable in format KEY=VALUE (can be used multiple times)')
def envs_update(system_id: str, env_name: str, file_path: Optional[str], vars: tuple):
    """Update an existing environment.
    
    Can read from stdin: cat .env | klug-secrets envs update --system main --env dev
    """
    file_contents = None
    
    if file_path:
        # Read from file
        with open(file_path, 'r') as f:
            file_contents = f.read()
    elif not vars:
        # If no --file and no --var, try reading from stdin
        if not sys.stdin.isatty():
            file_contents = sys.stdin.read()
    
    if file_contents:
        # Validate that the content is in VARNAME=varval format
        lines = file_contents.strip().split('\n')
        invalid_lines = []
        for i, line in enumerate(lines, 1):
            line = line.strip()
            # Skip empty lines and comments
            if not line or line.startswith('#'):
                continue
            # Check if line matches VARNAME=varval format
            if '=' not in line:
                invalid_lines.append((i, line))
            else:
                # Check that there's at least one character before the first '='
                key_part = line.split('=', 1)[0].strip()
                if not key_part:
                    invalid_lines.append((i, line))
        
        if invalid_lines:
            click.echo(click.style('Error: Invalid .env file format. All lines must be in VARNAME=varval format.', fg='red'), err=True)
            click.echo(click.style('Invalid lines:', fg='red'), err=True)
            for line_num, line_content in invalid_lines:
                click.echo(click.style(f'  Line {line_num}: {line_content}', fg='red'), err=True)
            sys.exit(1)
        
        # Use fileContents format for API (same as create)
        data = {'fileContents': file_contents}
        result = make_request('PUT', f'/envs/{system_id}/{env_name}', data=data)
        click.echo(click.style(f'✓ Environment "{system_id}/{env_name}" updated successfully', fg='green'))
        return
    
    # If no file_contents, use --var options
    env_vars = {}
    
    # Add vars from command line
    for var in vars:
        if '=' not in var:
            click.echo(click.style(f'Warning: Skipping invalid var format: {var}', fg='yellow'), err=True)
            continue
        key, value = var.split('=', 1)
        env_vars[key.strip()] = value.strip()
    
    if not env_vars:
        click.echo(click.style('Error: Must provide --file, --var, or pipe input from stdin', fg='red'), err=True)
        sys.exit(1)
    
    # Use env_vars format if we have individual vars
    data = {'env_vars': env_vars}
    result = make_request('PUT', f'/envs/{system_id}/{env_name}', data=data)
    click.echo(click.style(f'✓ Environment "{system_id}/{env_name}" updated successfully', fg='green'))


@envs.command('delete')
@click.option('--system', 'system_id', required=True, help='System ID')
@click.option('--env', 'env_name', required=True, help='Environment name')
@click.confirmation_option(prompt='Are you sure you want to delete this environment?')
def envs_delete(system_id: str, env_name: str):
    """Delete an environment."""
    result = make_request('DELETE', f'/envs/{system_id}/{env_name}')
    click.echo(click.style(f'✓ Environment "{system_id}/{env_name}" deleted successfully', fg='green'))


@envs.command('dump')
@click.option('--system', 'system_id', required=True, help='System ID')
@click.option('--env', 'env_name', required=True, help='Environment name')
def envs_dump(system_id: str, env_name: str):
    """Dump an environment to a .env file (saves to .env.<env_name>)."""
    # Get the environment
    result = make_request('GET', f'/envs/{system_id}/{env_name}')
    
    if 'env_vars' not in result:
        click.echo(click.style('Error: Environment variables not found in response', fg='red'), err=True)
        sys.exit(1)
    
    # Format as .env file
    env_vars = result['env_vars']
    env_file_content = []
    
    for key, value in sorted(env_vars.items()):
        # Escape special characters in value
        if isinstance(value, str):
            # If value contains newlines, spaces, or special chars, quote it
            if '\n' in value or ' ' in value or '#' in value or '=' in value:
                # Escape quotes and backslashes
                escaped_value = value.replace('\\', '\\\\').replace('"', '\\"')
                env_file_content.append(f'{key}="{escaped_value}"')
            else:
                env_file_content.append(f'{key}={value}')
        else:
            env_file_content.append(f'{key}={value}')
    
    # Write to file
    output_file = f'.env.{env_name}'
    try:
        with open(output_file, 'w') as f:
            f.write('\n'.join(env_file_content))
            if env_file_content:  # Add newline at end if there's content
                f.write('\n')
        
        click.echo(click.style(f'✓ Environment dumped to {output_file}', fg='green'))
    except IOError as e:
        click.echo(click.style(f'Error writing to {output_file}: {str(e)}', fg='red'), err=True)
        sys.exit(1)


if __name__ == '__main__':
    cli()

