use log::debug;
use rowan::TextRange;
use rue_ast::{AstFieldAccessExpr, AstNode};
use rue_diagnostic::DiagnosticKind;
use rue_hir::Value;

use crate::{
    Compiler, CompletionContext, Field, FieldResult, SyntaxField, SyntaxItemKind, compile_expr,
    compile_field,
};

pub fn compile_field_access_expr(ctx: &mut Compiler, access: &AstFieldAccessExpr) -> Value {
    let expr = if let Some(expr) = access.expr() {
        compile_expr(ctx, &expr, None)
    } else {
        debug!("Unresolved field access expr");
        ctx.builtins().unresolved.clone()
    };

    let Some(dot) = access.dot() else {
        debug!("Unresolved field access dot");
        return ctx.builtins().unresolved.clone();
    };

    ctx.add_syntax(
        SyntaxItemKind::CompletionContext(CompletionContext::StructFields {
            ty: expr.ty,
            specified_fields: None,
        }),
        TextRange::new(dot.text_range().end(), access.syntax().text_range().end()),
    );

    let Some(name) = access.field() else {
        debug!("Unresolved field access name");
        return ctx.builtins().unresolved.clone();
    };

    match compile_field(ctx, expr.clone(), &Field::Named(name.text())) {
        FieldResult::Value(value) => {
            ctx.add_syntax(
                SyntaxItemKind::FieldReference(SyntaxField {
                    name: name.text().to_string(),
                    container: expr.ty,
                    ty: value.ty,
                }),
                name.text_range(),
            );
            value
        }
        FieldResult::Unknown => {
            debug!("Unresolved field access due to unknown field");
            let type_name = ctx.type_name(expr.ty);
            ctx.diagnostic(
                &name,
                DiagnosticKind::UnknownField(name.text().to_string(), type_name),
            );
            ctx.builtins().unresolved.clone()
        }
        FieldResult::Error => {
            debug!("Unresolved field access due to missing field in underlying struct type");
            let type_name = ctx.type_name(expr.ty);
            ctx.diagnostic(
                &name,
                DiagnosticKind::MissingField(name.text().to_string(), type_name),
            );
            ctx.builtins().unresolved.clone()
        }
    }
}
