# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

from typing import Any

import libcst as cst
from libcst import parse_statement
from libcst._nodes.tests.base import CSTNodeTest, DummyIndentedBlock
from libcst.metadata import CodeRange
from libcst.testing.utils import data_provider


class SimpleStatementTest(CSTNodeTest):
    @data_provider(
        (
            # a single-element SimpleStatementLine
            {
                "node": cst.SimpleStatementLine((cst.Pass(),)),
                "code": "pass\n",
                "parser": parse_statement,
            },
            # a multi-element SimpleStatementLine
            {
                "node": cst.SimpleStatementLine(
                    (cst.Pass(semicolon=cst.Semicolon()), cst.Continue())
                ),
                "code": "pass;continue\n",
                "parser": parse_statement,
            },
            # a multi-element SimpleStatementLine with whitespace
            {
                "node": cst.SimpleStatementLine(
                    (
                        cst.Pass(
                            semicolon=cst.Semicolon(
                                whitespace_before=cst.SimpleWhitespace(" "),
                                whitespace_after=cst.SimpleWhitespace("  "),
                            )
                        ),
                        cst.Continue(),
                    )
                ),
                "code": "pass ;  continue\n",
                "parser": parse_statement,
            },
            # A more complicated SimpleStatementLine
            {
                "node": cst.SimpleStatementLine(
                    (
                        cst.Pass(semicolon=cst.Semicolon()),
                        cst.Continue(semicolon=cst.Semicolon()),
                        cst.Break(),
                    )
                ),
                "code": "pass;continue;break\n",
                "parser": parse_statement,
                "expected_position": CodeRange((1, 0), (1, 19)),
            },
            # a multi-element SimpleStatementLine, inferred semicolons
            {
                "node": cst.SimpleStatementLine(
                    (cst.Pass(), cst.Continue(), cst.Break())
                ),
                "code": "pass; continue; break\n",
                "parser": None,  # No test for parsing, since we are using sentinels.
            },
            # some expression statements
            {
                "node": cst.SimpleStatementLine((cst.Expr(cst.Name("None")),)),
                "code": "None\n",
                "parser": parse_statement,
            },
            {
                "node": cst.SimpleStatementLine((cst.Expr(cst.Name("True")),)),
                "code": "True\n",
                "parser": parse_statement,
            },
            {
                "node": cst.SimpleStatementLine((cst.Expr(cst.Name("False")),)),
                "code": "False\n",
                "parser": parse_statement,
            },
            {
                "node": cst.SimpleStatementLine((cst.Expr(cst.Ellipsis()),)),
                "code": "...\n",
                "parser": parse_statement,
            },
            # Test some numbers
            {
                "node": cst.SimpleStatementLine((cst.Expr(cst.Integer("5")),)),
                "code": "5\n",
                "parser": parse_statement,
            },
            {
                "node": cst.SimpleStatementLine((cst.Expr(cst.Float("5.5")),)),
                "code": "5.5\n",
                "parser": parse_statement,
            },
            {
                "node": cst.SimpleStatementLine((cst.Expr(cst.Imaginary("5j")),)),
                "code": "5j\n",
                "parser": parse_statement,
            },
            # Test some numbers with parens
            {
                "node": cst.SimpleStatementLine(
                    (
                        cst.Expr(
                            cst.Integer(
                                "5", lpar=(cst.LeftParen(),), rpar=(cst.RightParen(),)
                            )
                        ),
                    )
                ),
                "code": "(5)\n",
                "parser": parse_statement,
                "expected_position": CodeRange((1, 0), (1, 3)),
            },
            {
                "node": cst.SimpleStatementLine(
                    (
                        cst.Expr(
                            cst.Float(
                                "5.5", lpar=(cst.LeftParen(),), rpar=(cst.RightParen(),)
                            )
                        ),
                    )
                ),
                "code": "(5.5)\n",
                "parser": parse_statement,
            },
            {
                "node": cst.SimpleStatementLine(
                    (
                        cst.Expr(
                            cst.Imaginary(
                                "5j", lpar=(cst.LeftParen(),), rpar=(cst.RightParen(),)
                            )
                        ),
                    )
                ),
                "code": "(5j)\n",
                "parser": parse_statement,
            },
            # Test some strings
            {
                "node": cst.SimpleStatementLine((cst.Expr(cst.SimpleString('"abc"')),)),
                "code": '"abc"\n',
                "parser": parse_statement,
            },
            {
                "node": cst.SimpleStatementLine(
                    (
                        cst.Expr(
                            cst.ConcatenatedString(
                                cst.SimpleString('"abc"'), cst.SimpleString('"def"')
                            )
                        ),
                    )
                ),
                "code": '"abc""def"\n',
                "parser": parse_statement,
            },
            {
                "node": cst.SimpleStatementLine(
                    (
                        cst.Expr(
                            cst.ConcatenatedString(
                                left=cst.SimpleString('"abc"'),
                                whitespace_between=cst.SimpleWhitespace(" "),
                                right=cst.ConcatenatedString(
                                    left=cst.SimpleString('"def"'),
                                    whitespace_between=cst.SimpleWhitespace(" "),
                                    right=cst.SimpleString('"ghi"'),
                                ),
                            )
                        ),
                    )
                ),
                "code": '"abc" "def" "ghi"\n',
                "parser": parse_statement,
                "expected_position": CodeRange((1, 0), (1, 17)),
            },
            # Test parenthesis rules
            {
                "node": cst.SimpleStatementLine(
                    (
                        cst.Expr(
                            cst.Ellipsis(
                                lpar=(cst.LeftParen(),), rpar=(cst.RightParen(),)
                            )
                        ),
                    )
                ),
                "code": "(...)\n",
                "parser": parse_statement,
            },
            # Test parenthesis with whitespace ownership
            {
                "node": cst.SimpleStatementLine(
                    (
                        cst.Expr(
                            cst.Ellipsis(
                                lpar=(
                                    cst.LeftParen(
                                        whitespace_after=cst.SimpleWhitespace(" ")
                                    ),
                                ),
                                rpar=(
                                    cst.RightParen(
                                        whitespace_before=cst.SimpleWhitespace(" ")
                                    ),
                                ),
                            )
                        ),
                    )
                ),
                "code": "( ... )\n",
                "parser": parse_statement,
            },
            {
                "node": cst.SimpleStatementLine(
                    (
                        cst.Expr(
                            cst.Ellipsis(
                                lpar=(
                                    cst.LeftParen(
                                        whitespace_after=cst.SimpleWhitespace(" ")
                                    ),
                                    cst.LeftParen(
                                        whitespace_after=cst.SimpleWhitespace("  ")
                                    ),
                                    cst.LeftParen(
                                        whitespace_after=cst.SimpleWhitespace("   ")
                                    ),
                                ),
                                rpar=(
                                    cst.RightParen(
                                        whitespace_before=cst.SimpleWhitespace("   ")
                                    ),
                                    cst.RightParen(
                                        whitespace_before=cst.SimpleWhitespace("  ")
                                    ),
                                    cst.RightParen(
                                        whitespace_before=cst.SimpleWhitespace(" ")
                                    ),
                                ),
                            )
                        ),
                    )
                ),
                "code": "( (  (   ...   )  ) )\n",
                "parser": parse_statement,
                "expected_position": CodeRange((1, 0), (1, 21)),
            },
            # Test parenthesis rules with expressions
            {
                "node": cst.SimpleStatementLine(
                    (
                        cst.Expr(
                            cst.Ellipsis(
                                lpar=(
                                    cst.LeftParen(
                                        whitespace_after=cst.ParenthesizedWhitespace(
                                            first_line=cst.TrailingWhitespace(),
                                            empty_lines=(
                                                cst.EmptyLine(
                                                    comment=cst.Comment(
                                                        "# Wow, a comment!"
                                                    )
                                                ),
                                            ),
                                            indent=True,
                                            last_line=cst.SimpleWhitespace("    "),
                                        )
                                    ),
                                ),
                                rpar=(
                                    cst.RightParen(
                                        whitespace_before=cst.ParenthesizedWhitespace(
                                            first_line=cst.TrailingWhitespace(),
                                            empty_lines=(),
                                            indent=True,
                                            last_line=cst.SimpleWhitespace(""),
                                        )
                                    ),
                                ),
                            )
                        ),
                    )
                ),
                "code": "(\n# Wow, a comment!\n    ...\n)\n",
                "parser": parse_statement,
                "expected_position": CodeRange((1, 0), (4, 1)),
            },
            # test trailing whitespace
            {
                "node": cst.SimpleStatementLine(
                    (cst.Pass(),),
                    trailing_whitespace=cst.TrailingWhitespace(
                        whitespace=cst.SimpleWhitespace("  "),
                        comment=cst.Comment("# trailing comment"),
                    ),
                ),
                "code": "pass  # trailing comment\n",
                "parser": parse_statement,
                "expected_position": CodeRange((1, 0), (1, 4)),
            },
            # test leading comment
            {
                "node": cst.SimpleStatementLine(
                    (cst.Pass(),),
                    leading_lines=(cst.EmptyLine(comment=cst.Comment("# comment")),),
                ),
                "code": "# comment\npass\n",
                "parser": parse_statement,
                "expected_position": CodeRange((2, 0), (2, 4)),
            },
            # test indentation
            {
                "node": DummyIndentedBlock(
                    "    ",
                    cst.SimpleStatementLine(
                        (cst.Pass(),),
                        leading_lines=(
                            cst.EmptyLine(comment=cst.Comment("# comment")),
                        ),
                    ),
                ),
                "code": "    # comment\n    pass\n",
                "expected_position": CodeRange((2, 4), (2, 8)),
            },
            # test suite variant
            {
                "node": cst.SimpleStatementSuite((cst.Pass(),)),
                "code": " pass\n",
                "expected_position": CodeRange((1, 1), (1, 5)),
            },
            {
                "node": cst.SimpleStatementSuite(
                    (cst.Pass(),), leading_whitespace=cst.SimpleWhitespace("")
                ),
                "code": "pass\n",
                "expected_position": CodeRange((1, 0), (1, 4)),
            },
        )
    )
    def test_valid(self, **kwargs: Any) -> None:
        self.validate_node(**kwargs)
