import json
import os
import sys
from decimal import Decimal
from pathlib import Path

import boto3
import psycopg
import psycopg.conninfo

# backend以下をimportする  # noqa: ERA001
current_file = Path(__file__)
project_root = current_file.parent.joinpath("..", "..", "..").resolve()
backend_path = project_root.joinpath("backend").resolve()
for p in (str(backend_path), str(project_root)):
    if p not in sys.path:
        sys.path.insert(0, p)

from backend.src.__types.index import Price  # noqa: E402

POSTGRES_HOST = os.getenv("POSTGRES_HOST")
POSTGRES_PORT = os.getenv("POSTGRES_PORT")
POSTGRES_USER = os.getenv("POSTGRES_USER")
POSTGRES_PASSWORD = os.getenv("POSTGRES_PASSWORD")
POSTGRES_DB = os.getenv("POSTGRES_DB")

pricing_client = boto3.client("pricing", region_name="us-east-1")


def main() -> None:
    print("Start collecting pricing data...")
    __collect_aws_pricing_data()
    __collect_gcp_pricing_data()
    __collect_azure_pricing_data()
    print("Pricing data collection completed.")


def __collect_aws_pricing_data() -> None:
    print("Collecting AWS pricing data...")
    __collect_all_bedrock_pricing_by_api()
    __collect_other_aws_services_pricing_by_api()
    __collect_aws_pricing_data_without_api()


def __collect_all_bedrock_pricing_by_api() -> list[Price]:
    """全てのBedrock料金データを収集"""
    print("Collecting all Bedrock pricing data from AWS Pricing API...")
    paginator = pricing_client.get_paginator("get_products")
    response_iterator = paginator.paginate(
        ServiceCode="AmazonBedrock",
    )

    pricing_data = []
    for page in response_iterator:
        price_list_raw = page.get("PriceList", [])
        pricing_data.extend([json.loads(item) for item in price_list_raw])

    print(f"Collected {len(pricing_data)} pricing items from AWS Pricing API.")

    # JSON文字列をPriceオブジェクトのリストに変換
    price_list = __convert_to_price_list(pricing_data)

    # データベースに保存
    if price_list:
        __upsert_pricing_data(price_list)

    return price_list


def __convert_to_price_list(pricing_data: list[dict]) -> list[Price]:
    """AWS Pricing APIのJSONデータをPriceオブジェクトのリストに変換"""
    price_list = []
    # current_date = datetime.now(tz=timezone.utc)  # Price APIはUTCで帰ってくる

    # for price_item in pricing_data:
    #     try:
    #         attributes = price_item.get("product", {}).get("attributes", {})
    #         region_code = attributes.get("regionCode")
    #         usage_type = attributes.get("usagetype")

    #         if not region_code or not usage_type:
    #             continue

    #         # OnDemandのみ対応
    #         on_demand = price_item.get("terms", {}).get("OnDemand")

    #         if not on_demand:
    #             continue

    #         # 最新の有効な料金データを探す
    #         latest_effective_date = None
    #         latest_price = None

    #         for term_value in on_demand.values():
    #             effective_date_str = term_value.get("effectiveDate")
    #             if not effective_date_str:
    #                 continue

    #             try:
    #                 effective_date = datetime.fromisoformat(effective_date_str.replace("Z", "+00:00"))

    #                 # effective_dateが今日より未来の場合はスキップ
    #                 if effective_date > current_date:
    #                     continue

    #                 # effective_dateが古いものはスキップ
    #                 if latest_effective_date is not None and effective_date < latest_effective_date:
    #                     continue

    #                 price_dimensions = term_value.get("priceDimensions", {})
    #                 for price_dimension in price_dimensions.values():
    #                     unit = price_dimension.get("unit")
    #                     price_usd = price_dimension.get("pricePerUnit", {}).get("USD", "0")

    #                     if price_usd and price_usd != "0" and Decimal(price_usd) > 0:
    #                         # token単位の課金のみ対応
    #                         if unit == "1K tokens":
    #                             price_per_1k_tokens = Decimal(price_usd)
    #                             latest_price = price_per_1k_tokens / Decimal(1000)
    #                             latest_effective_date = effective_date

    #                         if unit == "TextUnit":
    #                             latest_price = Decimal(price_usd)
    #                             latest_effective_date = effective_date

    #             except (ValueError, KeyError):
    #                 continue

    #         if latest_price is not None:
    #             normalized_usage_type = usage_type.replace(".", "-")
    #             code = f"aws.{region_code}.{normalized_usage_type}"
    #             price_list.append(Price(code=code, price=latest_price))

    #     except Exception as e:
    #         print(f"データ処理エラー: {e}")
    #         continue

    # print(f"変換完了: {len(price_list)}件のPriceオブジェクトを作成")
    return price_list


def __collect_other_aws_services_pricing_by_api():
    """他のサービスの料金データを収集"""
    # TODO: 他のサービスの料金データ収集ロジックを実装


def __collect_aws_pricing_data_without_api() -> None:
    """APIを使用せずにAWS料金データを収集"""
    # TODO: APIを使用せずにAWS料金データを収集するロジックを実装
    print("Collecting AWS pricing data without API...")
    price_list = [
        # https://aws.amazon.com/jp/bedrock/pricing/
        Price(code="aws.ap-northeast-1.APN1-Claude3Haiku-input-tokens", price=Decimal("0.00000025")),
        Price(code="aws.ap-northeast-1.APN1-Claude3Haiku-output-tokens", price=Decimal("0.00000125")),
        Price(code="aws.ap-northeast-1.APN1-Claude3-5Sonnet-input-tokens", price=Decimal("0.000003")),
        Price(code="aws.ap-northeast-1.APN1-Claude3-5Sonnet-output-tokens", price=Decimal("0.000015")),
        Price(code="aws.ap-northeast-1.APN1-Claude4Sonnet-input-tokens", price=Decimal("0.000003")),
        Price(code="aws.ap-northeast-1.APN1-Claude4Sonnet-output-tokens", price=Decimal("0.000015")),
        Price(code="aws.ap-northeast-1.APN1-Claude4Sonnet-cache-read-input-tokens", price=Decimal("0.0000003")),
        Price(code="aws.ap-northeast-1.APN1-Claude4Sonnet-cache-write-input-tokens", price=Decimal("0.00000375")),
        Price(code="aws.ap-northeast-1.APN1-NovaLite-input-tokens", price=Decimal("0.000000072")),
        Price(code="aws.ap-northeast-1.APN1-NovaLite-output-tokens", price=Decimal("0.000000288")),
        Price(code="aws.ap-northeast-1.APN1-NovaLite-cache-read-input-token-count", price=Decimal("0.000000018")),
        Price(code="aws.ap-northeast-1.APN1-Claude4-5Haiku-input-tokens", price=Decimal("0.0000011")),
        Price(code="aws.ap-northeast-1.APN1-Claude4-5Haiku-output-tokens", price=Decimal("0.0000055")),
        Price(code="aws.ap-northeast-1.APN1-Claude4-5Haiku-cache-read-input-tokens", price=Decimal("0.00000011")),
        Price(code="aws.ap-northeast-1.APN1-Claude4-5Haiku-cache-write-input-tokens", price=Decimal("0.000001375")),
        Price(code="aws.ap-northeast-1.APN1-Claude4-5Sonnet-input-tokens", price=Decimal("0.0000033")),
        Price(code="aws.ap-northeast-1.APN1-Claude4-5Sonnet-output-tokens", price=Decimal("0.0000165")),
        Price(code="aws.ap-northeast-1.APN1-Claude4-5Sonnet-cache-read-input-tokens", price=Decimal("0.00000033")),
        Price(code="aws.ap-northeast-1.APN1-Claude4-5Sonnet-cache-write-input-tokens", price=Decimal("0.000004125")),
        Price(code="aws.ap-northeast-1.APN1-Claude4-6Sonnet-input-tokens", price=Decimal("0.0000033")),
        Price(code="aws.ap-northeast-1.APN1-Claude4-6Sonnet-output-tokens", price=Decimal("0.0000165")),
        Price(code="aws.ap-northeast-1.APN1-Claude4-6Sonnet-cache-read-input-tokens", price=Decimal("0.00000033")),
        Price(code="aws.ap-northeast-1.APN1-Claude4-6Sonnet-cache-write-input-tokens", price=Decimal("0.000004125")),
        # NOTE: gemma-3-27b は、AWS Pricing API で取得した gemma系モデル の UsageType に
        #       "-standard" サフィックスが含まれていたため、他のモデルと異なりこのサフィックスを付与している。
        Price(code="aws.ap-northeast-1.APN1-google-gemma-3-27b-it-input-tokens-standard", price=Decimal("0.00000028")),
        Price(code="aws.ap-northeast-1.APN1-google-gemma-3-27b-it-output-tokens-standard", price=Decimal("0.00000046")),
        Price(code="aws.us-east-1.USE1-Claude3-7Sonnet-input-tokens", price=Decimal("0.000003")),
        Price(code="aws.us-east-1.USE1-Claude3-7Sonnet-output-tokens", price=Decimal("0.000015")),
        Price(code="aws.us-east-1.USE1-Claude3-7Sonnet-cache-read-input-tokens", price=Decimal("0.0000003")),
        Price(code="aws.us-east-1.USE1-Claude3-7Sonnet-cache-write-input-tokens", price=Decimal("0.00000375")),
        Price(code="aws.us-east-1.USE1-Claude4-5Sonnet-input-tokens", price=Decimal("0.0000033")),
        Price(code="aws.us-east-1.USE1-Claude4-5Sonnet-output-tokens", price=Decimal("0.0000165")),
        Price(code="aws.us-east-1.USE1-Claude4-5Sonnet-cache-read-input-tokens", price=Decimal("0.00000033")),
        Price(code="aws.us-east-1.USE1-Claude4-5Sonnet-cache-write-input-tokens", price=Decimal("0.000004125")),
        Price(code="aws.us-east-1.USE1-Claude4-6Sonnet-input-tokens", price=Decimal("0.0000033")),
        Price(code="aws.us-east-1.USE1-Claude4-6Sonnet-output-tokens", price=Decimal("0.0000165")),
        Price(code="aws.us-east-1.USE1-Claude4-6Sonnet-cache-read-input-tokens", price=Decimal("0.00000033")),
        Price(code="aws.us-east-1.USE1-Claude4-6Sonnet-5m-cache-write-input-tokens", price=Decimal("0.000004125")),
        Price(code="aws.us-east-1.USE1-Claude4-6Sonnet-1h-cache-write-input-tokens", price=Decimal("0.0000066")),
    ]
    __upsert_pricing_data(price_list)


def __collect_gcp_pricing_data() -> None:
    """GCPの料金データを収集"""
    # TODO: GCPの料金データ収集ロジックを実装
    print("Collecting GCP pricing data...")
    price_list = [
        # https://cloud.google.com/skus?currency=USD&filter=34CE-11E0-D24D&hl=ja
        Price(
            code="gc.global.Vertex-AI.Gemini-1-5-Flash-Text-Output-Predictions",
            price=Decimal("0.000000075"),
        ),
        # https://cloud.google.com/skus?currency=USD&filter=C2B6-7B34-0A1B&hl=ja
        Price(code="gc.global.Vertex-AI.Gemini-1-5-Flash-Text-Input-Predictions", price=Decimal("0.00000001875")),
        # https://cloud.google.com/skus?currency=USD&filter=4D69-506A-5D33&hl=ja
        Price(code="gc.global.Vertex-AI.Gemini-2-0-Flash-Lite-Text-Output-Predictions", price=Decimal("0.0000003")),
        # https://cloud.google.com/skus?currency=USD&filter=CF72-F84C-8E3B&hl=ja
        Price(code="gc.global.Vertex-AI.Gemini-2-0-Flash-Lite-Text-Input-Predictions", price=Decimal("0.000000075")),
        # https://cloud.google.com/skus?currency=USD&filter=A121-E2B5-1418&hl=ja
        Price(code="gc.global.Vertex-AI.Gemini-2-5-Pro-Text-Input-Predictions", price=Decimal("0.00000125")),
        # https://cloud.google.com/skus?currency=USD&filter=5DA2-3F77-1CA5&hl=ja
        Price(code="gc.global.Vertex-AI.Gemini-2-5-Pro-Text-Output-Predictions", price=Decimal("0.00001")),
        # https://cloud.google.com/skus?currency=USD&filter=FDAB-647C-5A22&hl=ja
        Price(code="gc.global.Vertex-AI.Gemini-2-5-Flash-GA-Text-Input-Predictions", price=Decimal("0.0000003")),
        # https://cloud.google.com/skus?currency=USD&filter=AF56-1BF9-492A&hl=ja
        Price(code="gc.global.Vertex-AI.Gemini-2-5-Flash-GA-Text-Output-Predictions", price=Decimal("0.0000025")),
        # 製品企画と相談の結果、Gemini 2.5 flash liteは、Gemini 2.0 flash lite の単価と同じにする
        Price(code="gc.global.Vertex-AI.Gemini-2-5-Flash-Lite-Text-Output-Predictions", price=Decimal("0.0000003")),
        Price(code="gc.global.Vertex-AI.Gemini-2-5-Flash-Lite-Text-Input-Predictions", price=Decimal("0.000000075")),
    ]
    __upsert_pricing_data(price_list)


def __collect_azure_pricing_data() -> None:
    """Azureの料金データを収集"""
    # TODO: Azureの料金データ収集ロジックを実装
    print("Collecting Azure pricing data...")
    price_list = [
        # https://azure.microsoft.com/ja-jp/pricing/details/azure-openai/
        Price(code="azure.japaneast.Consumption.gpt-4-1-Inp-glbl", price=Decimal("0.000002")),
        Price(code="azure.japaneast.Consumption.gpt-4-1-Outp-glbl", price=Decimal("0.000008")),
        Price(code="azure.japaneast.Consumption.gpt-4-1-mini-Inp-glbl", price=Decimal("0.0000004")),
        Price(code="azure.japaneast.Consumption.gpt-4-1-mini-Outp-glbl", price=Decimal("0.0000016")),
        Price(code="azure.japaneast.Consumption.GPT-5-Inpt-Glbl", price=Decimal("0.00000125")),
        Price(code="azure.japaneast.Consumption.GPT-5-outpt-Glbl", price=Decimal("0.00001")),
        Price(code="azure.japaneast.Consumption.GPT-5-Mini-Inpt-Glbl", price=Decimal("0.00000025")),
        Price(code="azure.japaneast.Consumption.GPT-5-Mini-outpt-Glbl", price=Decimal("0.000002")),
        Price(code="azure.japaneast.Consumption.54-nano-Inp-Gl", price=Decimal("0.0000002")),
        Price(code="azure.japaneast.Consumption.54-nano-Opt-Gl", price=Decimal("0.00000125")),
        Price(code="azure.japaneast.Consumption.54-nano-cd-Inp-Gl", price=Decimal("0.00000002")),
        Price(code="azure.japaneast.Consumption.54-mini-Inp-Gl", price=Decimal("0.00000075")),
        Price(code="azure.japaneast.Consumption.54-mini-Opt-Gl", price=Decimal("0.0000045")),
        Price(code="azure.japaneast.Consumption.54-mini-cd-Inp-Gl", price=Decimal("0.000000075")),
        Price(code="azure.japaneast.Consumption.54-inp-Gl", price=Decimal("0.0000025")),
        Price(code="azure.japaneast.Consumption.54-opt-Gl", price=Decimal("0.000015")),
        Price(code="azure.japaneast.Consumption.54-cd-inp-Gl", price=Decimal("0.00000025")),
    ]
    __upsert_pricing_data(price_list)


def __upsert_pricing_data(price_list: list[Price]) -> None:
    """収集した料金データをDBに挿入"""
    postgres_url = psycopg.conninfo.make_conninfo(
        host=POSTGRES_HOST,
        port=POSTGRES_PORT,
        user=POSTGRES_USER,
        password=POSTGRES_PASSWORD,
        dbname=POSTGRES_DB,
    )
    with psycopg.connect(postgres_url) as conn, conn.cursor() as cur:
        cur.executemany(
            """INSERT INTO prices (code, price)
            VALUES (%s, %s)
            ON CONFLICT (code) DO UPDATE SET
            price = EXCLUDED.price,
            updated_at = CURRENT_TIMESTAMP AT TIME ZONE 'Asia/Tokyo'""",
            [(price.code, price.price) for price in price_list],
        )
        conn.commit()
        print("Upserted pricing data into the database.")


if __name__ == "__main__":
    main()
