# WARNING: this file is auto-generated by 'async_to_sync.py'
# from the original file 'test_tstring_async.py'
# DO NOT CHANGE! Change the original file instead.
from random import random
from typing import Any

import pytest

import psycopg
from psycopg import sql
from psycopg.pq import Format

from .acompat import skip_async
from .test_sql import no_e
from .test_adapt import make_dumper

vstr = "hello"
vint = 16


def test_connection_no_params(conn):
    with pytest.raises(TypeError):
        conn.execute(t"select 1", [])


def test_cursor_no_params(conn):
    cur = conn.cursor()
    with pytest.raises(TypeError):
        cur.execute(t"select 1", [])


def test_connection_execute(conn):
    cur = conn.execute(t"select {vstr}")
    assert cur.fetchone() == ("hello",)
    assert cur._query.query == b"select $1"
    assert cur._query.params == [b"hello"]
    assert cur._query.types == (0,)


@pytest.mark.parametrize(
    "t", [t"select {vstr!a}", t"select {vstr!r}", t"select {vstr!s}"]
)
def test_no_conversion(conn, t):
    with pytest.raises(TypeError):
        conn.execute(t)


@pytest.mark.parametrize(
    "t, fmt",
    [
        (t"select {vint}", Format.BINARY),
        (t"select {vint:s}", Format.BINARY),
        (t"select {vint:t}", Format.TEXT),
        (t"select {vint:b}", Format.BINARY),
    ],
)
def test_format(conn, t, fmt):
    cur = conn.execute(t)
    assert cur.fetchone() == (16,)
    assert cur._query.query == b"select $1"
    assert cur._query.types == (psycopg.adapters.types["smallint"].oid,)
    assert cur._query.params == [b"\x00\x10" if fmt == Format.BINARY else b"16"]
    assert cur._query.formats == [fmt]


def test_format_bad(conn):
    with pytest.raises(psycopg.ProgrammingError, match="format 'x' not supported"):
        conn.execute(t"select {vint:x}")


def test_expression(conn):
    cur = conn.execute(t"select {vint * 2}")
    assert cur.fetchone() == (32,)
    assert cur._query.query == b"select $1"
    assert cur._query.types == (psycopg.adapters.types["smallint"].oid,)
    assert cur._query.params == [b"\x00 "]
    assert cur._query.formats == [Format.BINARY]


def test_format_identifier(conn):
    f1 = "foo-bar"
    f2 = "baz"
    cur = conn.execute(t"select {vint} as {f1:i}, {vint * 2:t} as {f2:i}")
    assert cur.fetchone() == (16, 32)
    assert cur._query.query == b'select $1 as "foo-bar", $2 as "baz"'
    assert cur._query.types == (psycopg.adapters.types["smallint"].oid,) * 2
    assert cur._query.params == [b"\x00\x10", b"32"]
    assert cur._query.formats == [Format.BINARY, Format.TEXT]


def test_format_literal(conn):
    f1 = "foo-bar"
    f2 = "baz"
    cur = conn.execute(t"select {vint * 2:l} as {f1:i}, {vint:t} as {f2:i}")
    assert cur.fetchone() == (32, 16)
    assert cur._query.query == b'select 32 as "foo-bar", $1 as "baz"'
    assert cur._query.types == (psycopg.adapters.types["smallint"].oid,)
    assert cur._query.params == [b"16"]
    assert cur._query.formats == [Format.TEXT]


def test_nested(conn):
    part = t"{vint} as foo"
    cur = conn.execute(t"select {part:q}")
    assert cur.fetchone() == (16,)
    assert cur._query.query == b"select $1 as foo"
    assert cur._query.types == (psycopg.adapters.types["smallint"].oid,)
    assert cur._query.params == [b"\x00\x10"]
    assert cur._query.formats == [Format.BINARY]

    with pytest.raises(psycopg.ProgrammingError, match="Template.*':q'"):
        cur = conn.execute(t"select {part}")


def test_scope(conn):
    t = t"select "
    for i, name in enumerate(("foo", "bar", "baz")):
        if i:
            t += t", "
        t += t"{i} as {name:i}"

    cur = conn.execute(t)
    assert cur.fetchone() == (0, 1, 2)
    assert cur.description[0].name == "foo"
    assert cur.description[2].name == "baz"


def test_no_reuse(conn):
    t = t"select {vint}, {vint}"
    cur = conn.execute(t)
    assert cur.fetchone() == (vint, vint)
    assert b"$2" in cur._query.query


def test_volatile(conn):
    t = t"select {random()}, {random()}"
    cur = conn.execute(t)
    rec = cur.fetchone()
    assert rec[0] != rec[1]
    assert b"$2" in cur._query.query


def test_sql(conn):
    part = sql.SQL("foo")
    cur = conn.execute(t"select {vint} as {part:q}")
    assert cur.fetchone() == (16,)
    assert cur._query.query == b"select $1 as foo"

    with pytest.raises(psycopg.ProgrammingError, match="sql\\.SQL.*':q'"):
        conn.execute(t"select {vint} as {part:i}")


def test_sql_composed(conn):
    part = sql.SQL("{} as {}").format(vint, sql.Identifier("foo"))
    cur = conn.execute(t"select {part:q}")
    assert cur.fetchone() == (16,)
    assert cur._query.query == b'select 16 as "foo"'

    with pytest.raises(psycopg.ProgrammingError, match="sql\\.Composed.*':q'"):
        conn.execute(t"select {part}")


def test_sql_identifier(conn):
    part = sql.Identifier("foo")
    cur = conn.execute(t"select {vint} as {part:i}")
    assert cur.fetchone() == (16,)
    assert cur._query.query == b'select $1 as "foo"'

    with pytest.raises(psycopg.ProgrammingError, match="sql\\.Identifier.*':i'"):
        conn.execute(t"select {vint} as {part}")


def test_sql_literal(conn):
    lit = sql.Literal(42)
    cur = conn.execute(t"select {lit:l} as foo")
    assert cur.fetchone() == (42,)
    assert cur._query.query == b"select 42 as foo"

    with pytest.raises(psycopg.ProgrammingError, match="sql\\.Literal.*':l'"):
        conn.execute(t"select {lit} as foo")


def test_sql_placeholder(conn):
    part = sql.Placeholder("foo")
    with pytest.raises(psycopg.ProgrammingError, match="Placeholder not supported"):
        conn.execute(t"select {part}")


@pytest.mark.xfail(reason="Template.join() needed")
def test_template_join(conn):
    ts = [t"{i} as {name:i}" for i, name in enumerate(("foo", "bar", "baz"))]
    fields = t",".join(ts)  # type: ignore[attr-defined]
    cur = conn.execute(t"select {fields}")
    assert cur.fetchone() == (0, 1, 2)
    assert cur.description[0].name == "foo"
    assert cur.description[2].name == "baz"


def test_sql_join(conn):
    ts = [t"{i} as {name:i}" for i, name in enumerate(("foo", "bar", "baz"))]
    fields = sql.SQL(",").join(ts)
    cur = conn.execute(t"select {fields:q}")
    assert cur.fetchone() == (0, 1, 2)
    assert cur.description[0].name == "foo"
    assert cur.description[2].name == "baz"


@skip_async
def test_sql_join_mixed_types():
    ts: list[Any] = [t"{i} as {name:i}" for i, name in enumerate(("foo", "bar"))]
    ts.append(sql.Literal(2))
    for it in (ts, reversed(ts)):
        with pytest.raises(TypeError, match="Template and Literal"):
            sql.SQL(",").join(it)


def test_copy(conn):
    cur = conn.cursor()
    with cur.copy(t"copy (select * from generate_series(1, {3})) to stdout") as copy:
        data = list(copy.rows())
    assert data == [("1",), ("2",), ("3",)]


def test_client_cursor(conn):
    cur = psycopg.ClientCursor(conn)
    cur.execute(t"select {vint}, {vstr} as {vstr:i}")
    assert cur.fetchone() == (vint, vstr)
    assert cur.description
    assert cur.description[1].name == vstr
    assert cur._query
    assert str(vint) in cur._query.query.decode()
    assert str(vint) == cur._query.params[0].decode()  # type: ignore
    assert f"'{vstr}'" in cur._query.query.decode()
    assert f"'{vstr}'" in cur._query.params[1].decode()  # type: ignore


def test_mogrify(conn):
    cur = psycopg.ClientCursor(conn)
    res = cur.mogrify(t"select {vint}, {vstr} as {vstr:i}")
    assert res == "select 16, 'hello' as \"hello\""


def test_raw_cursor(conn):
    cur = psycopg.RawCursor(conn)
    with pytest.raises(psycopg.NotSupportedError):
        cur.execute(t"select {vint}, {vstr} as {vstr:i}")


def test_server_cursor(conn):
    with psycopg.ServerCursor(conn, "test") as cur:
        cur.execute(t"select {vint}, {vstr} as {vstr:i}")
        assert cur.fetchone() == (vint, vstr)
        assert cur.description
        assert cur.description[1].name == vstr
        assert cur._query
        assert b"$2" in cur._query.query
        assert b"$3" not in cur._query.query


@skip_async
def test_as_string():
    query = sql.as_string(t"select {vstr}")
    assert query == no_e("select 'hello'")


def test_as_string_context(conn):
    conn.adapters.register_dumper(str, make_dumper("1"))
    query = sql.as_string(t"select {vstr}", context=conn)
    assert query == no_e("select 'hello1'")


@skip_async
def test_as_bytes():
    query = sql.as_bytes(t"select {vstr}")
    assert query == no_e(b"select 'hello'")


def test_as_bytes_context(conn):
    conn.adapters.register_dumper(str, make_dumper("1"))
    query = sql.as_bytes(t"select {vstr}", context=conn)
    assert query == no_e(b"select 'hello1'")
