//! Document symbol extraction for outline view

use tower_lsp::lsp_types::{
    DocumentSymbol,
    Position,
    Range,
    SymbolKind,
};

use crate::{
    position::PositionTracker,
    syntax::ast::*,
};

/// Extract document symbols from AST
pub fn extract_symbols_from_ast(
    ast: &File,
    positions: &mut PositionTracker,
) -> Vec<DocumentSymbol> {
    let mut symbols = Vec::new();

    for decl in &ast.declarations {
        if let Some(symbol) = extract_declaration_symbol(decl, positions) {
            symbols.push(symbol);
        }
    }

    symbols
}

/// Extract a symbol from a declaration
#[allow(deprecated)]
fn extract_declaration_symbol(
    decl: &Declaration,
    positions: &mut PositionTracker,
) -> Option<DocumentSymbol> {
    let (name, kind, span, children) = match decl {
        | Declaration::Character(c) => (
            c.name.clone(),
            SymbolKind::CLASS,
            c.span.clone(),
            extract_field_symbols(&c.fields, positions),
        ),
        | Declaration::Template(t) => (
            t.name.clone(),
            SymbolKind::INTERFACE,
            t.span.clone(),
            extract_field_symbols(&t.fields, positions),
        ),
        | Declaration::LifeArc(l) => (
            l.name.clone(),
            SymbolKind::FUNCTION,
            l.span.clone(),
            extract_state_symbols(&l.states, positions),
        ),
        | Declaration::Schedule(s) => (
            s.name.clone(),
            SymbolKind::EVENT,
            s.span.clone(),
            extract_block_symbols(&s.blocks, positions),
        ),
        | Declaration::Behavior(b) => (
            b.name.clone(),
            SymbolKind::MODULE,
            b.span.clone(),
            Vec::new(), // Behavior tree structure is too complex for symbol tree
        ),
        | Declaration::Institution(i) => (
            i.name.clone(),
            SymbolKind::MODULE,
            i.span.clone(),
            extract_field_symbols(&i.fields, positions),
        ),
        | Declaration::Relationship(r) => (
            r.name.clone(),
            SymbolKind::STRUCT,
            r.span.clone(),
            extract_field_symbols(&r.fields, positions),
        ),
        | Declaration::Location(l) => (
            l.name.clone(),
            SymbolKind::CONSTANT,
            l.span.clone(),
            extract_field_symbols(&l.fields, positions),
        ),
        | Declaration::Species(s) => (
            s.name.clone(),
            SymbolKind::CLASS,
            s.span.clone(),
            extract_field_symbols(&s.fields, positions),
        ),
        | Declaration::Use(_) => return None, // Use statements don't create symbols
        | Declaration::Concept(_) | Declaration::SubConcept(_) | Declaration::Definition(_) => {
            return None
        }, // TODO: Implement symbols for type system
    };

    let (start_line, start_col) = positions.offset_to_position(span.start);
    let (end_line, end_col) = positions.offset_to_position(span.end);

    // Selection range is just the name
    let name_end_offset = span.start + name.len();
    let (name_end_line, name_end_col) = positions.offset_to_position(name_end_offset);

    Some(DocumentSymbol {
        name: name.clone(),
        detail: None,
        kind,
        tags: None,
        deprecated: None,
        range: Range {
            start: Position {
                line: start_line as u32,
                character: start_col as u32,
            },
            end: Position {
                line: end_line as u32,
                character: end_col as u32,
            },
        },
        selection_range: Range {
            start: Position {
                line: start_line as u32,
                character: start_col as u32,
            },
            end: Position {
                line: name_end_line as u32,
                character: name_end_col as u32,
            },
        },
        children: if children.is_empty() {
            None
        } else {
            Some(children)
        },
    })
}

/// Extract symbols from field declarations
#[allow(deprecated)]
fn extract_field_symbols(fields: &[Field], positions: &mut PositionTracker) -> Vec<DocumentSymbol> {
    fields
        .iter()
        .map(|field| {
            let (start_line, start_col) = positions.offset_to_position(field.span.start);
            let (end_line, end_col) = positions.offset_to_position(field.span.end);
            let name_end = field.span.start + field.name.len();
            let (name_end_line, name_end_col) = positions.offset_to_position(name_end);

            DocumentSymbol {
                name: field.name.clone(),
                detail: None,
                kind: SymbolKind::FIELD,
                tags: None,
                deprecated: None,
                range: Range {
                    start: Position {
                        line: start_line as u32,
                        character: start_col as u32,
                    },
                    end: Position {
                        line: end_line as u32,
                        character: end_col as u32,
                    },
                },
                selection_range: Range {
                    start: Position {
                        line: start_line as u32,
                        character: start_col as u32,
                    },
                    end: Position {
                        line: name_end_line as u32,
                        character: name_end_col as u32,
                    },
                },
                children: None,
            }
        })
        .collect()
}

/// Extract symbols from life arc states
#[allow(deprecated)]
fn extract_state_symbols(
    states: &[ArcState],
    positions: &mut PositionTracker,
) -> Vec<DocumentSymbol> {
    states
        .iter()
        .map(|state| {
            let (start_line, start_col) = positions.offset_to_position(state.span.start);
            let (end_line, end_col) = positions.offset_to_position(state.span.end);
            let name_end = state.span.start + state.name.len();
            let (name_end_line, name_end_col) = positions.offset_to_position(name_end);

            DocumentSymbol {
                name: state.name.clone(),
                detail: Some("state".to_string()),
                kind: SymbolKind::PROPERTY,
                tags: None,
                deprecated: None,
                range: Range {
                    start: Position {
                        line: start_line as u32,
                        character: start_col as u32,
                    },
                    end: Position {
                        line: end_line as u32,
                        character: end_col as u32,
                    },
                },
                selection_range: Range {
                    start: Position {
                        line: start_line as u32,
                        character: start_col as u32,
                    },
                    end: Position {
                        line: name_end_line as u32,
                        character: name_end_col as u32,
                    },
                },
                children: None,
            }
        })
        .collect()
}

/// Extract symbols from schedule blocks
#[allow(deprecated)]
fn extract_block_symbols(
    blocks: &[ScheduleBlock],
    positions: &mut PositionTracker,
) -> Vec<DocumentSymbol> {
    blocks
        .iter()
        .map(|block| {
            let (start_line, start_col) = positions.offset_to_position(block.span.start);
            let (end_line, end_col) = positions.offset_to_position(block.span.end);

            // For blocks, the "name" is the activity
            let name = block.activity.clone();
            let name_end = block.span.start + name.len();
            let (name_end_line, name_end_col) = positions.offset_to_position(name_end);

            DocumentSymbol {
                name,
                detail: Some(format!(
                    "{:02}:{:02}-{:02}:{:02}",
                    block.start.hour, block.start.minute, block.end.hour, block.end.minute
                )),
                kind: SymbolKind::PROPERTY,
                tags: None,
                deprecated: None,
                range: Range {
                    start: Position {
                        line: start_line as u32,
                        character: start_col as u32,
                    },
                    end: Position {
                        line: end_line as u32,
                        character: end_col as u32,
                    },
                },
                selection_range: Range {
                    start: Position {
                        line: start_line as u32,
                        character: start_col as u32,
                    },
                    end: Position {
                        line: name_end_line as u32,
                        character: name_end_col as u32,
                    },
                },
                children: None,
            }
        })
        .collect()
}
