"""Project management commands"""
import click
import json
import time
from tabulate import tabulate


@click.group()
def project():
    """Project management commands"""
    pass


@project.command('list')
@click.pass_context
def list_projects(ctx):
    """List all projects"""
    r = ctx.obj['redis']
    
    # 先收集所有项目 ID
    project_ids = []
    for key in r.scan_iter('hfs:project:*', count=100):
        key_str = key.decode() if isinstance(key, bytes) else key
        if ':stats' in key_str or ':accounts' in key_str:
            continue
        project_id = key_str.replace('hfs:project:', '')
        project_ids.append(project_id)
    
    if not project_ids:
        click.echo("No projects found")
        return
    
    # 批量获取项目数据
    projects = []
    for project_id in project_ids:
        proj_data = r.get(f'hfs:project:{project_id}')
        if not proj_data:
            continue
        
        proj = json.loads(proj_data)
        
        # 使用 keys 而不是 scan_iter（更快）
        node_keys = r.keys(f'hfs:node:{project_id}:*')
        node_count = len(node_keys) if node_keys else 0
        
        # 从项目统计获取 space 数量（避免扫描所有 space）
        stats_data = r.get(f'hfs:project:{project_id}:stats')
        space_count = 0
        if stats_data:
            stats = json.loads(stats_data)
            space_count = stats.get('spaces_running', 0)
        
        projects.append([
            project_id,
            proj.get('scene', 'production'),
            node_count,
            space_count,
            time.strftime('%Y-%m-%d', time.localtime(proj.get('created_at', 0))) if proj.get('created_at') else '-'
        ])
    
    if projects:
        click.echo(tabulate(projects, headers=['Project ID', 'Scene', 'Nodes', 'Running', 'Created']))
    else:
        click.echo("No projects found")


@project.command('show')
@click.argument('project_id')
@click.pass_context
def show_project(ctx, project_id):
    """Show project details"""
    r = ctx.obj['redis']
    
    key = f'hfs:project:{project_id}'
    data = r.get(key)
    
    if not data:
        click.echo(f"Project not found: {project_id}", err=True)
        ctx.exit(1)
    
    proj = json.loads(data)
    
    click.echo(f"\n📋 Project: {project_id}\n")
    click.echo(f"Scene: {proj.get('scene', 'production')}")
    
    if proj.get('required_nodes'):
        click.echo(f"Required Nodes: {proj.get('required_nodes')}")
    
    # Show config
    config = proj.get('config', {})
    if config:
        click.echo(f"\n⚙️  Configuration:")
        for key, value in config.items():
            click.echo(f"   {key}: {value}")
    
    # Show start script
    start_script = proj.get('start_script', {})
    if start_script:
        click.echo(f"\n🚀 Start Script:")
        if start_script.get('type') == 'inline':
            click.echo(f"   {start_script.get('inline', '')}")
    
    # List nodes
    nodes = []
    for node_key in r.scan_iter(f'hfs:node:{project_id}:*'):
        node_data = r.get(node_key)
        if node_data:
            node = json.loads(node_data)
            space_id = node.get('space_id') or node.get('space', '')
            nodes.append([
                node.get('id', ''),
                node.get('status', ''),
                space_id[:30] if space_id else '-'
            ])
    
    if nodes:
        click.echo(f"\n📦 Nodes ({len(nodes)}):")
        click.echo(tabulate(nodes, headers=['Node ID', 'Status', 'Space']))
    
    # Show stats
    stats_key = f'hfs:project:{project_id}:stats'
    stats_data = r.get(stats_key)
    if stats_data:
        stats = json.loads(stats_data)
        
        click.echo(f"\n📊 Statistics:")
        
        if stats.get('last_space_created'):
            elapsed = int(time.time() - stats['last_space_created'])
            click.echo(f"   Last space created: {elapsed}s ago")
        
        today = stats.get('today', {})
        if today:
            click.echo(f"   Today: created={today.get('spaces_created', 0)}, failed={today.get('spaces_failed', 0)}, reused={today.get('spaces_reused', 0)}")


@project.command('add-node')
@click.argument('project_id')
@click.argument('node_id')
@click.pass_context
def add_node(ctx, project_id, node_id):
    """Add node to project"""
    r = ctx.obj['redis']
    
    # Check project exists
    proj_key = f'hfs:project:{project_id}'
    if not r.exists(proj_key):
        click.echo(f"Project not found: {project_id}", err=True)
        ctx.exit(1)
    
    # Check node doesn't exist
    node_key = f'hfs:node:{project_id}:{node_id}'
    if r.exists(node_key):
        click.echo(f"Node already exists: {node_id}", err=True)
        ctx.exit(1)
    
    # Create node
    node_data = {
        'id': node_id,
        'project': project_id,
        'status': 'idle',
        'space': '',
        'created_at': int(time.time()),
        'updated_at': int(time.time())
    }
    
    r.set(node_key, json.dumps(node_data))
    
    click.echo(f"✅ Added node: {node_id} to {project_id}")


@project.command('remove-node')
@click.argument('project_id')
@click.argument('node_id')
@click.option('--force', is_flag=True, help='Force remove even if node has space')
@click.pass_context
def remove_node(ctx, project_id, node_id, force):
    """Remove node from project"""
    r = ctx.obj['redis']
    
    node_key = f'hfs:node:{project_id}:{node_id}'
    data = r.get(node_key)
    
    if not data:
        click.echo(f"Node not found: {node_id}", err=True)
        ctx.exit(1)
    
    node = json.loads(data)
    
    # Check if node has space
    space_id = node.get('space_id') or node.get('space', '')
    if space_id and not force:
        click.echo(f"Node has space bound: {space_id}", err=True)
        click.echo("Use --force to remove anyway", err=True)
        ctx.exit(1)
    
    r.delete(node_key)
    
    click.echo(f"✅ Removed node: {node_id} from {project_id}")
    
    if space_id:
        click.echo(f"⚠️  Warning: Space {space_id} was bound to this node")


@project.command('init')
@click.argument('config_file', type=click.Path(exists=True))
@click.option('--dry-run', is_flag=True, help='Preview without creating')
@click.pass_context
def init_project(ctx, config_file, dry_run):
    """Initialize project from YAML config"""
    r = ctx.obj['redis']
    
    import yaml
    with open(config_file) as f:
        cfg = yaml.safe_load(f)
    
    proj = cfg.get('project', {})
    if not proj.get('id'):
        click.echo("Error: project.id required in config", err=True)
        ctx.exit(1)
    
    project_id = proj['id']
    
    # Merge nodes from top-level if not in project
    if 'nodes' not in proj and 'nodes' in cfg:
        proj['nodes'] = cfg['nodes']
    
    if dry_run:
        click.echo(f"\n[DRY-RUN] Would create project: {project_id}")
        click.echo(f"  Scene: {proj.get('scene', 'production')}")
        
        nodes_config = proj.get('nodes', {})
        node_ids = nodes_config.get('ids', [])
        
        if nodes_config.get('pattern') and nodes_config.get('count'):
            pattern = nodes_config['pattern']
            count = nodes_config['count']
            node_ids = [pattern.replace('{num}', str(i)) for i in range(1, count + 1)]
        
        click.echo(f"  Nodes: {len(node_ids)}")
        for node_id in node_ids[:5]:
            click.echo(f"    - {node_id}")
        if len(node_ids) > 5:
            click.echo(f"    ... and {len(node_ids) - 5} more")
        
        return
    
    # Create project
    from .bootstrap import _create_project
    _create_project(r, proj)
    
    click.echo(f"\n✅ Project initialized: {project_id}")


@project.command('update')
@click.argument('config_file', type=click.Path(exists=True))
@click.pass_context
def update_project(ctx, config_file):
    """Update project from YAML config"""
    r = ctx.obj['redis']
    
    import yaml
    with open(config_file) as f:
        cfg = yaml.safe_load(f)
    
    proj = cfg.get('project', {})
    if not proj.get('id'):
        click.echo("Error: project.id required in config", err=True)
        ctx.exit(1)
    
    project_id = proj['id']
    proj_key = f'hfs:project:{project_id}'
    
    if not r.exists(proj_key):
        click.echo(f"Project not found: {project_id}", err=True)
        click.echo("Use 'project init' to create new project", err=True)
        ctx.exit(1)
    
    # Update project data
    project_data = {
        'id': project_id,
        'scene': proj.get('scene', 'production'),
        'updated_at': int(time.time())
    }
    
    if proj.get('description'):
        project_data['description'] = proj['description']
    if proj.get('required_nodes'):
        project_data['required_nodes'] = proj['required_nodes']
    if proj.get('config'):
        project_data['config'] = proj['config']
    if proj.get('start_script'):
        project_data['start_script'] = proj['start_script']
    if proj.get('stop_script'):
        project_data['stop_script'] = proj['stop_script']
    
    # Preserve created_at if exists
    old_data = r.get(proj_key)
    if old_data:
        old_proj = json.loads(old_data)
        if old_proj.get('created_at'):
            project_data['created_at'] = old_proj['created_at']
    
    r.set(proj_key, json.dumps(project_data))
    
    click.echo(f"✅ Updated project: {project_id}")


@project.command('export')
@click.argument('project_id')
@click.argument('output_file', type=click.Path())
@click.pass_context
def export_project(ctx, project_id, output_file):
    """Export project to YAML config"""
    r = ctx.obj['redis']
    
    proj_key = f'hfs:project:{project_id}'
    data = r.get(proj_key)
    
    if not data:
        click.echo(f"Project not found: {project_id}", err=True)
        ctx.exit(1)
    
    proj = json.loads(data)
    
    # Get nodes
    node_ids = []
    for key in r.scan_iter(f'hfs:node:{project_id}:*'):
        node_data = r.get(key)
        if node_data:
            node = json.loads(node_data)
            node_ids.append(node.get('id'))
    
    # Build config
    config = {
        'project': {
            'id': project_id,
            'scene': proj.get('scene', 'production'),
        },
        'nodes': {
            'ids': sorted(node_ids)
        }
    }
    
    if proj.get('description'):
        config['project']['description'] = proj['description']
    if proj.get('required_nodes'):
        config['project']['required_nodes'] = proj['required_nodes']
    if proj.get('config'):
        config['project']['config'] = proj['config']
    if proj.get('start_script'):
        config['project']['start_script'] = proj['start_script']
    if proj.get('stop_script'):
        config['project']['stop_script'] = proj['stop_script']
    
    # Write to file
    import yaml
    with open(output_file, 'w') as f:
        yaml.dump(config, f, default_flow_style=False, sort_keys=False)
    
    click.echo(f"✅ Exported to: {output_file}")


@project.command('delete')
@click.argument('project_id')
@click.option('--force', is_flag=True, help='Force delete even with running spaces')
@click.pass_context
def delete_project(ctx, project_id, force):
    """Delete project and all its nodes"""
    r = ctx.obj['redis']
    
    proj_key = f'hfs:project:{project_id}'
    if not r.exists(proj_key):
        click.echo(f"Project not found: {project_id}", err=True)
        ctx.exit(1)
    
    # Check for running spaces
    running_count = 0
    for key in r.scan_iter('hfs:space:*'):
        data = r.get(key)
        if data:
            space = json.loads(data)
            if space.get('project_id') == project_id and space.get('status') in ('running', 'starting'):
                running_count += 1
    
    if running_count > 0 and not force:
        click.echo(f"Project has {running_count} running space(s)", err=True)
        click.echo("Use --force to delete anyway", err=True)
        ctx.exit(1)
    
    # Delete nodes
    node_count = 0
    for key in r.scan_iter(f'hfs:node:{project_id}:*'):
        r.delete(key)
        node_count += 1
    
    # Delete project
    r.delete(proj_key)
    
    # Delete stats
    r.delete(f'hfs:project:{project_id}:stats')
    r.delete(f'hfs:project:{project_id}:accounts')
    
    click.echo(f"✅ Deleted project: {project_id}")
    click.echo(f"   Deleted {node_count} nodes")
    
    if running_count > 0:
        click.echo(f"   ⚠️  Warning: {running_count} space(s) were running")
