#![allow(clippy::unwrap_used)]

use crate::constants::{LAST_COLUMN, LAST_ROW};
use crate::expressions::lexer::LexerError;
use crate::expressions::{
    lexer::{Lexer, LexerMode},
    token::TokenType::*,
    types::ParsedReference,
};
use crate::language::get_language;
use crate::locale::get_locale;

fn new_lexer(formula: &str) -> Lexer {
    let locale = get_locale("en").unwrap();
    let language = get_language("en").unwrap();
    Lexer::new(formula, LexerMode::A1, locale, language)
}

#[test]
fn test_range() {
    let mut lx = new_lexer("C4:D4");
    assert_eq!(
        lx.next_token(),
        Range {
            sheet: None,
            left: ParsedReference {
                column: 3,
                row: 4,
                absolute_column: false,
                absolute_row: false,
            },
            right: ParsedReference {
                column: 4,
                row: 4,
                absolute_column: false,
                absolute_row: false,
            }
        }
    );
    assert_eq!(lx.next_token(), EOF);
}

#[test]
fn test_range_absolute_column() {
    let mut lx = new_lexer("$A1:B$4");
    assert_eq!(
        lx.next_token(),
        Range {
            sheet: None,
            left: ParsedReference {
                column: 1,
                row: 1,
                absolute_column: true,
                absolute_row: false,
            },
            right: ParsedReference {
                column: 2,
                row: 4,
                absolute_column: false,
                absolute_row: true,
            }
        }
    );
    assert_eq!(lx.next_token(), EOF);
}

#[test]
fn test_range_with_sheet() {
    let mut lx = new_lexer("Sheet1!A1:B4");
    assert_eq!(
        lx.next_token(),
        Range {
            sheet: Some("Sheet1".to_string()),
            left: ParsedReference {
                column: 1,
                row: 1,
                absolute_column: false,
                absolute_row: false,
            },
            right: ParsedReference {
                column: 2,
                row: 4,
                absolute_column: false,
                absolute_row: false,
            }
        }
    );
    assert_eq!(lx.next_token(), EOF);
}

#[test]
fn test_range_with_sheet_with_space() {
    let mut lx = new_lexer("'New sheet'!$A$1:B44");
    assert_eq!(
        lx.next_token(),
        Range {
            sheet: Some("New sheet".to_string()),
            left: ParsedReference {
                column: 1,
                row: 1,
                absolute_column: true,
                absolute_row: true,
            },
            right: ParsedReference {
                column: 2,
                row: 44,
                absolute_column: false,
                absolute_row: false,
            }
        }
    );
    assert_eq!(lx.next_token(), EOF);
}

#[test]
fn test_range_error() {
    let mut lx = new_lexer("'Sheet 1'!3.4:5");
    assert_eq!(
        lx.next_token(),
        Illegal(LexerError {
            position: 10,
            message: "Expecting reference in range".to_string(),
        })
    );
    assert_eq!(lx.next_token(), EOF);

    let mut lx = new_lexer("'Sheet 1'!3:A2");
    assert_eq!(
        lx.next_token(),
        Illegal(LexerError {
            position: 14,
            message: "Error parsing Range".to_string()
        })
    );
    assert_eq!(lx.next_token(), EOF);

    let mut lx = new_lexer("'Sheet 1'!3:");
    assert_eq!(
        lx.next_token(),
        Illegal(LexerError {
            position: 12,
            message: "Error parsing Range".to_string()
        })
    );
    assert_eq!(lx.next_token(), EOF);
}

#[test]
fn test_range_column() {
    let mut lx = new_lexer("C:D");
    assert_eq!(
        lx.next_token(),
        Range {
            sheet: None,
            left: ParsedReference {
                column: 3,
                row: 1,
                absolute_column: false,
                absolute_row: true,
            },
            right: ParsedReference {
                column: 4,
                row: LAST_ROW,
                absolute_column: false,
                absolute_row: true,
            }
        }
    );
    assert_eq!(lx.next_token(), EOF);
}

#[test]
fn test_range_column_out_of_range() {
    let mut lx = new_lexer("C:XFE");
    assert_eq!(
        lx.next_token(),
        Illegal(LexerError {
            position: 5,
            message: "Column is not valid.".to_string(),
        })
    );
    assert_eq!(lx.next_token(), EOF);
}

#[test]
fn test_range_column_absolute1() {
    let mut lx = new_lexer("$C:D");
    assert_eq!(
        lx.next_token(),
        Range {
            sheet: None,
            left: ParsedReference {
                column: 3,
                row: 1,
                absolute_column: true,
                absolute_row: true,
            },
            right: ParsedReference {
                column: 4,
                row: LAST_ROW,
                absolute_column: false,
                absolute_row: true,
            }
        }
    );
    assert_eq!(lx.next_token(), EOF);
}

#[test]
fn test_range_column_absolute2() {
    let mut lx = new_lexer("$C:$AA");
    assert_eq!(
        lx.next_token(),
        Range {
            sheet: None,
            left: ParsedReference {
                column: 3,
                row: 1,
                absolute_column: true,
                absolute_row: true,
            },
            right: ParsedReference {
                column: 27,
                row: LAST_ROW,
                absolute_column: true,
                absolute_row: true,
            }
        }
    );
    assert_eq!(lx.next_token(), EOF);
}

#[test]
fn test_range_rows() {
    let mut lx = new_lexer("3:5");
    assert_eq!(
        lx.next_token(),
        Range {
            sheet: None,
            left: ParsedReference {
                column: 1,
                row: 3,
                absolute_column: true,
                absolute_row: false,
            },
            right: ParsedReference {
                column: LAST_COLUMN,
                row: 5,
                absolute_column: true,
                absolute_row: false,
            }
        }
    );
    assert_eq!(lx.next_token(), EOF);
}

#[test]
fn test_range_rows_absolute1() {
    let mut lx = new_lexer("$3:5");
    assert_eq!(
        lx.next_token(),
        Range {
            sheet: None,
            left: ParsedReference {
                column: 1,
                row: 3,
                absolute_column: true,
                absolute_row: true,
            },
            right: ParsedReference {
                column: LAST_COLUMN,
                row: 5,
                absolute_column: true,
                absolute_row: false,
            }
        }
    );
    assert_eq!(lx.next_token(), EOF);
}

#[test]
fn test_range_rows_absolute2() {
    let mut lx = new_lexer("$3:$55");
    assert_eq!(
        lx.next_token(),
        Range {
            sheet: None,
            left: ParsedReference {
                column: 1,
                row: 3,
                absolute_column: true,
                absolute_row: true,
            },
            right: ParsedReference {
                column: LAST_COLUMN,
                row: 55,
                absolute_column: true,
                absolute_row: true,
            }
        }
    );
    assert_eq!(lx.next_token(), EOF);
}

#[test]
fn test_range_column_sheet() {
    let mut lx = new_lexer("Sheet1!C:D");
    assert_eq!(
        lx.next_token(),
        Range {
            sheet: Some("Sheet1".to_string()),
            left: ParsedReference {
                column: 3,
                row: 1,
                absolute_column: false,
                absolute_row: true,
            },
            right: ParsedReference {
                column: 4,
                row: LAST_ROW,
                absolute_column: false,
                absolute_row: true,
            }
        }
    );
    assert_eq!(lx.next_token(), EOF);
}

#[test]
fn test_range_column_sheet_absolute() {
    let mut lx = new_lexer("Sheet1!$C:$D");
    assert_eq!(
        lx.next_token(),
        Range {
            sheet: Some("Sheet1".to_string()),
            left: ParsedReference {
                column: 3,
                row: 1,
                absolute_column: true,
                absolute_row: true,
            },
            right: ParsedReference {
                column: 4,
                row: LAST_ROW,
                absolute_column: true,
                absolute_row: true,
            }
        }
    );
    assert_eq!(lx.next_token(), EOF);

    let mut lx = new_lexer("'Woops ans'!$C:$D");
    assert_eq!(
        lx.next_token(),
        Range {
            sheet: Some("Woops ans".to_string()),
            left: ParsedReference {
                column: 3,
                row: 1,
                absolute_column: true,
                absolute_row: true,
            },
            right: ParsedReference {
                column: 4,
                row: LAST_ROW,
                absolute_column: true,
                absolute_row: true,
            }
        }
    );
    assert_eq!(lx.next_token(), EOF);
}

#[test]
fn test_range_rows_sheet() {
    let mut lx = new_lexer("'A new sheet'!3:5");
    assert_eq!(
        lx.next_token(),
        Range {
            sheet: Some("A new sheet".to_string()),
            left: ParsedReference {
                column: 1,
                row: 3,
                absolute_column: true,
                absolute_row: false,
            },
            right: ParsedReference {
                column: LAST_COLUMN,
                row: 5,
                absolute_column: true,
                absolute_row: false,
            }
        }
    );
    assert_eq!(lx.next_token(), EOF);
    let mut lx = new_lexer("Sheet12!3:5");
    assert_eq!(
        lx.next_token(),
        Range {
            sheet: Some("Sheet12".to_string()),
            left: ParsedReference {
                column: 1,
                row: 3,
                absolute_column: true,
                absolute_row: false,
            },
            right: ParsedReference {
                column: LAST_COLUMN,
                row: 5,
                absolute_column: true,
                absolute_row: false,
            }
        }
    );
    assert_eq!(lx.next_token(), EOF);
}
// Non ranges

#[test]
fn test_non_range_variable_name() {
    let mut lx = new_lexer("AB");
    assert_eq!(lx.next_token(), Ident("AB".to_string()));
    assert_eq!(lx.next_token(), EOF);
}

#[test]
fn test_non_range_invalid_variable_name() {
    let mut lx = new_lexer("$AB");
    assert!(matches!(lx.next_token(), Illegal(_)));
    assert_eq!(lx.next_token(), EOF);
}

#[test]
fn test_non_range_invalid_variable_name_a03() {
    let mut lx = new_lexer("A03");
    assert_eq!(
        lx.next_token(),
        Reference {
            sheet: None,
            row: 3,
            column: 1,
            absolute_column: false,
            absolute_row: false
        }
    );
    assert_eq!(lx.next_token(), EOF);
}

#[test]
fn test_non_range_invalid_variable_name_sheet1_a03() {
    let mut lx = new_lexer("Sheet1!A03");
    assert_eq!(
        lx.next_token(),
        Reference {
            sheet: Some("Sheet1".to_string()),
            row: 3,
            column: 1,
            absolute_column: false,
            absolute_row: false
        }
    );
    assert_eq!(lx.next_token(), EOF);
}

#[test]
fn test_range_rows_with_0() {
    let mut lx = new_lexer("03:05");
    assert_eq!(
        lx.next_token(),
        Range {
            sheet: None,
            left: ParsedReference {
                column: 1,
                row: 3,
                absolute_column: true,
                absolute_row: false,
            },
            right: ParsedReference {
                column: LAST_COLUMN,
                row: 5,
                absolute_column: true,
                absolute_row: false,
            }
        }
    );
    assert_eq!(lx.next_token(), EOF);
}

#[test]
fn test_range_incomplete_row() {
    let mut lx = new_lexer("R[");
    lx.set_lexer_mode(LexerMode::R1C1);
    assert!(matches!(lx.next_token(), Illegal(_)));
    assert_eq!(lx.next_token(), EOF);
}

#[test]
fn test_range_incomplete_column() {
    let mut lx = new_lexer("R[3][");
    lx.set_lexer_mode(LexerMode::R1C1);
    assert!(matches!(lx.next_token(), Illegal(_)));
    assert_eq!(lx.next_token(), EOF);
}

#[test]
fn range_operator() {
    let mut lx = new_lexer("A1:OFFSET(B1,1,2)");
    lx.set_lexer_mode(LexerMode::A1);
    assert!(matches!(lx.next_token(), Reference { .. }));
    assert!(matches!(lx.next_token(), Colon));
    assert!(matches!(lx.next_token(), Ident(_)));
    assert!(matches!(lx.next_token(), LeftParenthesis));
    assert!(matches!(lx.next_token(), Reference { .. }));
    assert_eq!(lx.next_token(), Comma);
    assert!(matches!(lx.next_token(), Number(_)));
    assert_eq!(lx.next_token(), Comma);
    assert!(matches!(lx.next_token(), Number(_)));
    assert!(matches!(lx.next_token(), RightParenthesis));
    assert_eq!(lx.next_token(), EOF);
}
