import asyncio
from inspect import unwrap
from typing import Optional
from aiohttp.client import ClientSession
from loguru import logger
from sqlalchemy import engine
from sqlmodel import Field, SQLModel, select
from avilla.core import Avilla
from avilla.core.launch import LaunchComponent
from avilla.core.service.session import BehaviourSession
from avilla.core.service.sqlmodel import EngineProvider, SqlmodelService
from avilla.core.stream import Stream
from avilla.core.utilles.mock import LaunchMock
from avilla.core.service.aiohttp import AiohttpClient, AiohttpService
from avilla.core.service.common.activities import content_read, set_header, get_status, respond, send
from avilla.core.service.common.behaviours import PostConnected, DataReceived
from avilla.core.service.common.http import HttpClient, HttpServer, WebsocketClient
from avilla.core.service.starlette import StarletteService
from avilla.core.service.uvicorn import UvicornService
from avilla.core.transformers import u8_string, json_decode
import random
from starlette.responses import PlainTextResponse
from sqlmodel.ext.asyncio.session import AsyncSession

mocker = LaunchMock(
    {},
    [
        StarletteService(),
        #UvicornService("127.0.0.1", 12680),
        AiohttpService(),
        SqlmodelService("sqlite+aiosqlite://")
    ],
)


async def test(_):
    http_interface: HttpClient = mocker.get_interface(HttpClient)
    print(http_interface)
    async with http_interface.get("https://httpbin.org/anything") as session:
        content_stream = await session.execute(content_read)
        r = await (content_stream | u8_string | json_decode())
        print(r)

    """
    ws = mocker.get_interface(WebsocketClient)
    async with ws.websocket_connect("ws://ws.ifelse.io/") as session:
        @session.expand(PostConnected)
        async def on_connected(interface, session, stat):
            stat['e'] = random.random()
    
        @session.expand(DataReceived)
        async def on_data(interface, session, stat, data):
            stat['e'] += random.random()
            logger.debug(f"{stat} {data}")

        session.prepared()
        async def task():
            for i in range(20):
                r = f"send {i} {random.random()}"
                logger.debug(r)
                await session.execute(send(r))
                await asyncio.sleep(1)

        asyncio.create_task(task())
        await asyncio.sleep(20)"""


async def mainline_test(_):
    http_server = mocker.get_interface(HttpServer)

    async with http_server.http_listen("/", ["get"]) as session:

        @session.expand(DataReceived)
        async def on_data(interface, session: BehaviourSession, stat: dict, data: Stream[bytes]):
            await session.execute_all(
                respond(PlainTextResponse("OK!!!")),
                set_header("Content-Type", "text/plain"),
                set_header("X-Test", "OK"),
            )

        session.prepared()
        await mocker.sigexit.wait()


async def statusbar_test(_):
    await asyncio.sleep(1)


async def mainline_test2(_):
    await asyncio.sleep(100)


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: Optional[int] = None


async def sqlmodel_test(avilla: LaunchMock):
    engine = avilla.get_interface(EngineProvider).get()
    async with AsyncSession(engine) as session:
        session.add(Hero(name="John", secret_name="Doe", age=20))
        session.add(Hero(name="Jane", secret_name="Doe", age=20))
        session.add(Hero(name="Jack", secret_name="Doe", age=20))   
        await session.commit()

    async with AsyncSession(engine) as session:
        heroes = await session.execute(select(Hero))
        for hero in heroes:
            print(hero)
    

mocker.new_launch_component(
    "test.main",
    set(),
    #mainline=mainline_test,
    # mainline=mainline_test2,
    mainline=sqlmodel_test
    #prepare=statusbar_test,
)


mocker.launch_blocking()

starlette.responses.PlainTextResponse sqlmodel.ext.asyncio.session.AsyncSession avilla.core.service.aiohttp.AiohttpService sqlmodel.select avilla.core.service.starlette.StarletteService avilla.core.service.sqlmodel.SqlmodelService asyncio.sleep avilla.core.service.common.activities.set_header avilla.core.transformers.json_decode sqlmodel.Field
[((3164, 3201), 'sqlmodel.Field', 'Field', ([], {'default': 'None', 'primary_key': '(True)'}), '(default=None, primary_key=True)\n', (3169, 3201), False, 'from sqlmodel import Field, SQLModel, select\n'), ((1176, 1194), 'avilla.core.service.starlette.StarletteService', 'StarletteService', ([], {}), '()\n', (1192, 1194), False, 'from avilla.core.service.starlette import StarletteService\n'), ((1249, 1265), 'avilla.core.service.aiohttp.AiohttpService', 'AiohttpService', ([], {}), '()\n', (1263, 1265), False, 'from avilla.core.service.aiohttp import AiohttpClient, AiohttpService\n'), ((1275, 1313), 'avilla.core.service.sqlmodel.SqlmodelService', 'SqlmodelService', (['"""sqlite+aiosqlite://"""'], {}), "('sqlite+aiosqlite://')\n", (1290, 1313), False, 'from avilla.core.service.sqlmodel import EngineProvider, SqlmodelService\n'), ((3027, 3043), 'asyncio.sleep', 'asyncio.sleep', (['(1)'], {}), '(1)\n', (3040, 3043), False, 'import asyncio\n'), ((3085, 3103), 'asyncio.sleep', 'asyncio.sleep', (['(100)'], {}), '(100)\n', (3098, 3103), False, 'import asyncio\n'), ((3385, 3405), 'sqlmodel.ext.asyncio.session.AsyncSession', 'AsyncSession', (['engine'], {}), '(engine)\n', (3397, 3405), False, 'from sqlmodel.ext.asyncio.session import AsyncSession\n'), ((3666, 3686), 'sqlmodel.ext.asyncio.session.AsyncSession', 'AsyncSession', (['engine'], {}), '(engine)\n', (3678, 3686), False, 'from sqlmodel.ext.asyncio.session import AsyncSession\n'), ((1623, 1636), 'avilla.core.transformers.json_decode', 'json_decode', ([], {}), '()\n', (1634, 1636), False, 'from avilla.core.transformers import u8_string, json_decode\n'), ((3738, 3750), 'sqlmodel.select', 'select', (['Hero'], {}), '(Hero)\n', (3744, 3750), False, 'from sqlmodel import Field, SQLModel, select\n'), ((2822, 2862), 'avilla.core.service.common.activities.set_header', 'set_header', (['"""Content-Type"""', '"""text/plain"""'], {}), "('Content-Type', 'text/plain')\n", (2832, 2862), False, 'from avilla.core.service.common.activities import content_read, set_header, get_status, respond, send\n'), ((2880, 2906), 'avilla.core.service.common.activities.set_header', 'set_header', (['"""X-Test"""', '"""OK"""'], {}), "('X-Test', 'OK')\n", (2890, 2906), False, 'from avilla.core.service.common.activities import content_read, set_header, get_status, respond, send\n'), ((2777, 2803), 'starlette.responses.PlainTextResponse', 'PlainTextResponse', (['"""OK!!!"""'], {}), "('OK!!!')\n", (2794, 2803), False, 'from starlette.responses import PlainTextResponse\n')]