# Flyto2 Core - Source Available License
# Copyright (c) 2025 Flyto2. All Rights Reserved.
#
# This source code is licensed under the Flyto2 Source Available License v1.0.
# Commercial use requires a license. See LICENSE for details.

"""
Search API Modules

Google Search and SerpAPI integration modules.
"""

import os
from typing import Any

import aiohttp

from ....base import BaseModule
from ....registry import register_module
from ....schema import compose, presets


@register_module(
    module_id='core.api.google_search',
    version='1.0.0',
    category='api',
    subcategory='api',
    tags=['api', 'search', 'google', 'official', 'ssrf_protected'],
    label='Google Search (API)',
    label_key='modules.api.google_search.label',
    description='Use Google Custom Search API to search keywords',
    description_key='modules.api.google_search.description',
    icon='Search',
    color='#4285F4',
    input_types=[],
    output_types=['json', 'array', 'api_response'],
    can_connect_to=['data.*', 'notify.*', 'file.*'],
    can_receive_from=['start', 'flow.*'],
    timeout_ms=30000,
    retryable=True,
    max_retries=3,
    concurrent_safe=True,
    requires_credentials=True,
    credential_keys=['GOOGLE_API_KEY', 'GOOGLE_CSE_ID'],
    handles_sensitive_data=False,
    required_permissions=['network.access'],
    params_schema=compose(
        presets.SEARCH_KEYWORD(placeholder='python tutorial'),
        presets.SEARCH_LIMIT(max_val=10),
    ),
    output_schema={
        'status': {'type': 'string', 'description': 'Operation status (success/error)',
                'description_key': 'modules.core.api.google_search.output.status.description'},
        'data': {'type': 'array', 'description': 'Output data from the operation',
                'description_key': 'modules.core.api.google_search.output.data.description'},
        'count': {'type': 'number', 'description': 'Number of items',
                'description_key': 'modules.core.api.google_search.output.count.description'},
        'total_results': {'type': 'number', 'optional': True, 'description': 'Total number of search results available',
                'description_key': 'modules.core.api.google_search.output.total_results.description'}
    },
    examples=[{
        'title': 'Search Python tutorials',
        'params': {
            'keyword': 'python tutorial',
            'limit': 10
        }
    }],
    author='Flyto Team',
    license='MIT'
)
class GoogleSearchAPIModule(BaseModule):
    """Google Search API Module - Use official Custom Search API"""

    module_name = "Google Search (API)"
    module_description = "Use Google Custom Search API to search keywords"
    required_permission = "api.search"

    def validate_params(self) -> None:
        if 'keyword' not in self.params:
            raise ValueError("Missing parameter: keyword")
        self.keyword = self.params['keyword']
        self.limit = self.params.get('limit', 10)

    async def execute(self) -> Any:
        api_key = os.getenv('GOOGLE_API_KEY')
        search_engine_id = os.getenv('GOOGLE_SEARCH_ENGINE_ID')

        if not api_key or not search_engine_id:
            return {
                "status": "error",
                "message": "Please set GOOGLE_API_KEY and GOOGLE_SEARCH_ENGINE_ID environment variables",
                "setup_guide": {
                    "step1": "Go to https://console.cloud.google.com/apis/credentials",
                    "step2": "Create API Key",
                    "step3": "Enable Custom Search API",
                    "step4": "Go to https://programmablesearchengine.google.com/",
                    "step5": "Create search engine and get Search Engine ID",
                    "step6": "Set environment variable: GOOGLE_API_KEY and GOOGLE_SEARCH_ENGINE_ID"
                }
            }

        url = "https://www.googleapis.com/customsearch/v1"
        params = {
            'key': api_key,
            'cx': search_engine_id,
            'q': self.keyword,
            'num': min(self.limit, 10)
        }

        async with aiohttp.ClientSession() as session:
            async with session.get(url, params=params) as response:
                if response.status != 200:
                    error_data = await response.json()
                    return {
                        "status": "error",
                        "message": f"API error: {error_data.get('error', {}).get('message', 'Unknown error')}"
                    }

                data = await response.json()

                results = []
                for item in data.get('items', []):
                    results.append({
                        'title': item.get('title'),
                        'url': item.get('link'),
                        'description': item.get('snippet')
                    })

                return {
                    "status": "success",
                    "data": results,
                    "count": len(results),
                    "total_results": data.get('searchInformation', {}).get('totalResults', 0)
                }


@register_module(
    module_id='core.api.serpapi_search',
    version='1.0.0',
    category='api',
    subcategory='api',
    tags=['api', 'search', 'google', 'serpapi', 'third-party', 'ssrf_protected'],
    label='Google Search (SerpAPI)',
    label_key='modules.api.serpapi_search.label',
    description='Use SerpAPI to search keywords (100 free searches/month)',
    description_key='modules.api.serpapi_search.description',
    icon='Search',
    color='#F39C12',
    input_types=[],
    output_types=['json', 'array', 'api_response'],
    can_connect_to=['data.*', 'notify.*', 'file.*'],
    can_receive_from=['start', 'flow.*'],
    timeout_ms=30000,
    retryable=True,
    max_retries=3,
    concurrent_safe=True,
    requires_credentials=True,
    credential_keys=['SERPAPI_KEY'],
    handles_sensitive_data=False,
    required_permissions=['network.access'],
    params_schema=compose(
        presets.SEARCH_KEYWORD(placeholder='python tutorial'),
        presets.SEARCH_LIMIT(),
    ),
    output_schema={
        'status': {'type': 'string', 'description': 'Operation status (success/error)'},
        'data': {'type': 'array', 'description': 'Output data from the operation'},
        'count': {'type': 'number', 'description': 'Number of items'}
    },
    examples=[{
        'title': 'Search with SerpAPI',
        'params': {
            'keyword': 'machine learning',
            'limit': 10
        }
    }],
    author='Flyto Team',
    license='MIT'
)
class SerpAPISearchModule(BaseModule):
    """SerpAPI Search Module - Use third-party API (with free tier)"""

    module_name = "Google Search (SerpAPI)"
    module_description = "Use SerpAPI to search keywords (100 free searches/month)"
    required_permission = "api.search"

    def validate_params(self) -> None:
        if 'keyword' not in self.params:
            raise ValueError("Missing parameter: keyword")
        self.keyword = self.params['keyword']
        self.limit = self.params.get('limit', 10)

    async def execute(self) -> Any:
        api_key = os.getenv('SERPAPI_KEY')

        if not api_key:
            return {
                "status": "error",
                "message": "Please set SERPAPI_KEY environment variable",
                "setup_guide": {
                    "step1": "Go to https://serpapi.com/",
                    "step2": "Register account (Free 100 searches per month)",
                    "step3": "Get API Key",
                    "step4": "Set environment variable: SERPAPI_KEY"
                }
            }

        url = "https://serpapi.com/search"
        params = {
            'api_key': api_key,
            'q': self.keyword,
            'num': self.limit,
            'engine': 'google'
        }

        async with aiohttp.ClientSession() as session:
            async with session.get(url, params=params) as response:
                if response.status != 200:
                    return {
                        "status": "error",
                        "message": f"API error: HTTP {response.status}"
                    }

                data = await response.json()

                results = []
                for item in data.get('organic_results', []):
                    results.append({
                        'title': item.get('title'),
                        'url': item.get('link'),
                        'description': item.get('snippet')
                    })

                return {
                    "status": "success",
                    "data": results,
                    "count": len(results)
                }
