"""Implementation of Rule L027."""

from sqlfluff.core.rules.base import LintResult
from sqlfluff.rules.L020 import Rule_L020
from sqlfluff.core.rules.doc_decorators import document_groups


@document_groups
class Rule_L027(Rule_L020):
    """References should be qualified if select has more than one referenced table/view.

    .. note::
       Except if they're present in a ``USING`` clause.

    **Anti-pattern**

    In this example, the reference ``vee`` has not been declared,
    and the variables ``a`` and ``b`` are potentially ambiguous.

    .. code-block:: sql

        SELECT a, b
        FROM foo
        LEFT JOIN vee ON vee.a = foo.a

    **Best practice**

    Add the references.

    .. code-block:: sql

        SELECT foo.a, vee.b
        FROM foo
        LEFT JOIN vee ON vee.a = foo.a
    """

    groups = ("all",)

    def _lint_references_and_aliases(
        self,
        table_aliases,
        standalone_aliases,
        references,
        col_aliases,
        using_cols,
        parent_select,
    ):
        # Do we have more than one? If so, all references should be qualified.
        if len(table_aliases) <= 1:
            return None
        # A buffer to keep any violations.
        violation_buff = []
        # Check all the references that we have.
        for r in references:
            this_ref_type = r.qualification()
            # Discard column aliases that
            # refer to the current column reference.
            col_alias_names = [
                c.alias_identifier_name
                for c in col_aliases
                if r not in c.column_reference_segments
            ]
            if (
                this_ref_type == "unqualified"
                # Allow unqualified columns that
                # are actually aliases defined
                # in a different select clause element.
                and r.raw not in col_alias_names
                # Allow columns defined in a USING expression.
                and r.raw not in using_cols
            ):
                violation_buff.append(
                    LintResult(
                        anchor=r,
                        description=f"Unqualified reference {r.raw!r} found in "
                        "select with more than one referenced table/view.",
                    )
                )

            all_table_aliases = [t.ref_str for t in table_aliases] + standalone_aliases

            # For qualified references, we want to check that the alias is actually
            # valid
            if (
                this_ref_type == "qualified"
                and list(r.iter_raw_references())[0].part not in all_table_aliases
            ):
                violation_buff.append(
                    LintResult(
                        anchor=r,
                        description=f"Qualified reference {r.raw!r} not found in "
                        f"available tables/view aliases {all_table_aliases} in select "
                        "with more than one referenced table/view.",
                    )
                )

        return violation_buff or None
