"""
{% if fragments %}
{{ resource_name.replace('_', ' ').title() }} resource with fragments support for large documents{{ ' (async version)' if is_async else '' }}.

This module implements TWO tables:
1. '{{ resource_name }}' - Main table (schema determined by your fetch_data function)
2. '{{ resource_name }}_fragments' - Fragments table (schema determined by your fetch_fragments_data function)

IMPORTANT: You define both table schemas through your returned data structure.
Zeeker does not enforce any specific field names or relationships.

The database is built using sqlite-utils with automatic schema detection.
{% else %}
{{ resource_name.replace('_', ' ').title() }} resource for fetching and processing data{{ ' (async version)' if is_async else '' }}.

This module should implement {{ 'an async ' if is_async else 'a ' }}fetch_data() function that returns
a list of dictionaries to be inserted into the '{{ resource_name }}' table.

The database is built using sqlite-utils, which provides:
• Automatic table creation from your data structure
• Type inference (integers → INTEGER, floats → REAL, strings → TEXT)
• JSON support for complex data (lists, dicts stored as JSON)
• Safe data insertion without SQL injection risks
{% endif %}
"""
{% if is_async %}
import asyncio
import aiohttp
{% endif %}
from sqlite_utils.db import Table, NotFoundError
{% if is_async %}
from typing import Optional, List, Dict, Any, Awaitable
{% else %}
from typing import Optional, List, Dict, Any
{% endif %}

{% if not fragments %}
# Optional: Import common utilities from zeeker-common package
# Install with: uv add zeeker-common
# from zeeker_common import get_hash_id, get_jina_reader_content, async_retry
{% endif %}

{{ 'async ' if is_async else '' }}def fetch_data(existing_table: Optional[Table]) -> List[Dict[str, Any]]:
    """
{% if fragments %}
    {{ 'Async f' if is_async else 'F' }}etch data for the {{ resource_name }} table.

    Args:
        existing_table: sqlite-utils Table object if table exists, None for new table
                       Use this to check for existing data and avoid duplicates

    Returns:
        List[Dict[str, Any]]: Records for the main table

    IMPORTANT - Schema Considerations:
    Your FIRST fetch_data() call determines the column types permanently!
    sqlite-utils infers types from the first ~100 records and locks them in.

    You have complete freedom to define your schema. Common patterns:
    - Simple: {"id": 1, "title": "Doc 1", "content": "..."}
    - Metadata focused: {"id": 1, "title": "Doc 1", "source": "...", "date": "..."}
    - Complex: {"id": 1, "title": "Doc 1", "metadata": {"tags": ["tag1"]}, "status": "active"}
{% if not is_async %}

    CRITICAL - Duplicate Handling:
    Your function MUST NOT return records with IDs that already exist in the database.
    sqlite-utils will throw a UNIQUE constraint error if you do!

    Example usage:
        if existing_table:
            # REQUIRED: Get existing IDs to avoid duplicates
            existing_ids = {row["id"] for row in existing_table.rows}
            print(f"Found {len(existing_ids)} existing records")

            # Fetch fresh data and filter duplicates
            fresh_data = get_documents_from_source()
            new_records = [
                record for record in fresh_data
                if record["id"] not in existing_ids
            ]
            print(f"Adding {len(new_records)} new records")

            # Optional: Access metadata for incremental updates
            db = existing_table.db
            if "_zeeker_updates" in db.table_names():
                updates_table = db["_zeeker_updates"]
                try:
                    metadata = updates_table.get(existing_table.name)
                    last_updated = metadata["last_updated"]  # ISO datetime string
                    record_count = metadata["record_count"]  # Current row count
                    print(f"Last updated: {last_updated}, Records: {record_count}")
                except NotFoundError:
                    print("No metadata found - first run")

            return new_records
        else:
            print("Creating new table")
            return get_documents_from_source()
{% else %}

    Accessing metadata for incremental updates:
        if existing_table:
            db = existing_table.db
            if "_zeeker_updates" in db.table_names():
                updates_table = db["_zeeker_updates"]
                try:
                    metadata = updates_table.get(existing_table.name)
                    last_updated = metadata["last_updated"]  # ISO datetime string
                    record_count = metadata["record_count"]  # Current row count
                    print(f"Last updated: {last_updated}, Records: {record_count}")
                except NotFoundError:
                    print("No metadata found - first run")
{% endif %}
{% else %}
    {{ 'Async f' if is_async else 'F' }}etch data for the {{ resource_name }} table.

    Args:
        existing_table: sqlite-utils Table object if table exists, None for new table
                       Use this to check for existing data and avoid duplicates

    Returns:
        List[Dict[str, Any]]: List of records to insert into database

    IMPORTANT - Schema Considerations:
    Your FIRST fetch_data() call determines the column types permanently!
    sqlite-utils infers types from the first ~100 records and locks them in.
    Later runs cannot change existing column types, only add new columns.

    Python Type → SQLite Column Type:
    • int          → INTEGER
    • float        → REAL
    • str          → TEXT
    • bool         → INTEGER (stored as 0/1)
    • dict/list    → TEXT (stored as JSON)
    • None values  → Can cause type inference issues

    Best Practices:
    1. Make sure your first batch has correct Python types
    2. Use consistent data types across all records
    3. Avoid None/null values in key columns on first run
    4. Use float (not int) for numbers that might have decimals later

    CRITICAL - Duplicate Handling:
    Your function MUST NOT return records with IDs that already exist in the database.
    sqlite-utils will throw a UNIQUE constraint error if you do!

    Example usage:
        if existing_table:
            # REQUIRED: Get existing IDs to avoid duplicates
            existing_ids = {row["id"] for row in existing_table.rows}
            print(f"Found {len(existing_ids)} existing records")

            # Fetch fresh data from your {{ 'async ' if is_async else '' }}source
            fresh_data = {{ 'await ' if is_async else '' }}fetch_from_api()  # Your {{ 'async ' if is_async else '' }}data source

            # CRITICAL: Filter out existing records
            new_records = [
                record for record in fresh_data
                if record["id"] not in existing_ids
            ]
            print(f"Adding {len(new_records)} new records, skipping {len(fresh_data) - len(new_records)} duplicates")

            # Optional: Access metadata for time-based incremental updates
            db = existing_table.db
            if "_zeeker_updates" in db.table_names():
                updates_table = db["_zeeker_updates"]
                try:
                    metadata = updates_table.get(existing_table.name)
                    last_updated = metadata["last_updated"]  # ISO datetime string
                    print(f"Last updated: {last_updated}")
                    # Use last_updated for incremental fetching
                except NotFoundError:
                    print("No metadata found - first run")

            return new_records
        else:
            # Fresh table - CRITICAL: Set schema correctly with first batch!
            print("Creating new table")
            return {{ 'await ' if is_async else '' }}fetch_from_api()  # Get all data for first run
{% endif %}
    """
{% if is_async and not fragments %}
    # TODO: Implement your async data fetching logic here
    # This could be:
    # - Async API calls (aiohttp.get, httpx.get, etc.)
    # - Async file reading (aiofiles.open, etc.)
    # - Async database queries (asyncpg, aiomysql, etc.)
    # - Async web scraping (playwright, selenium, etc.)
    # - Any other async data source

    # Example async API call using aiohttp with duplicate handling
    async with aiohttp.ClientSession() as session:
        # Replace with your actual API endpoint
        async with session.get("https://api.example.com/data") as response:
            if response.status == 200:
                api_data = await response.json()
                # Process API data into standard format
                raw_data = [
                    # Example data showing proper types for schema inference:
                    {
                        "id": 1,                           # int → INTEGER (good for primary keys)
                        "title": "Example Title",          # str → TEXT
                        "score": 85.5,                     # float → REAL (use float even for whole numbers!)
                        "view_count": 100,                 # int → INTEGER
                        "is_published": True,              # bool → INTEGER (0/1)
                        "created_date": "2024-01-15",      # str → TEXT (ISO date format recommended)
                        "tags": ["news", "technology"],    # list → TEXT (stored as JSON)
                        "metadata": {"priority": "high"},  # dict → TEXT (stored as JSON)
                    },
                    # Add more example records with same structure...
                ]

                # Handle duplicates properly
                if existing_table:
                    existing_ids = {row["id"] for row in existing_table.rows}
                    raw_data = [record for record in raw_data if record["id"] not in existing_ids]
                    print(f"{{ resource_name }}: Adding {len(raw_data)} new records")

                return raw_data
            else:
                print(f"API request failed with status {response.status}")
                return []

    # Fallback should never be reached due to above return statements
    return []
{% elif is_async and fragments %}
    # TODO: Implement your async data fetching logic
    # This is just an example - replace with your actual schema and data

    # Example async document fetching
    async with aiohttp.ClientSession() as session:
        async with session.get("https://api.example.com/documents") as response:
            if response.status == 200:
                documents = await response.json()
                return [
                    {
                        "id": doc["id"],  # Required: some kind of identifier
                        "title": doc["title"],  # Your field names and types
                        "content": doc["content"],  # You decide what goes in main vs fragments
                        "source": doc.get("source", "api"),
                        "created_date": doc.get("created", "2024-01-01"),
                        # Add any other fields your project needs
                    }
                    for doc in documents
                ]

    # Fallback example data
    return [
        {
            "id": 1,  # Required: some kind of identifier
            "title": "Example Document",  # Your field names and types
            "content": "Document content...",  # You decide what goes in main vs fragments
            # Add any other fields your project needs
        },
        # Add more records...
    ]
{% elif not is_async and not fragments %}
    # TODO: Implement your data fetching logic here
    # This could be:
    # - API calls (requests.get, etc.)
    # - File reading (CSV, JSON, XML, etc.)
    # - Database queries (from other sources)
    # - Web scraping (BeautifulSoup, Scrapy, etc.)
    # - Any other data source

    # Example implementation with proper duplicate handling:
    raw_data = [
        # Example data showing proper types for schema inference:
        {
            "id": 1,                           # int → INTEGER (good for primary keys)
            "title": "Example Title",          # str → TEXT
            "score": 85.5,                     # float → REAL (use float even for whole numbers!)
            "view_count": 100,                 # int → INTEGER
            "is_published": True,              # bool → INTEGER (0/1)
            "created_date": "2024-01-15",      # str → TEXT (ISO date format recommended)
            "tags": ["news", "technology"],    # list → TEXT (stored as JSON)
            "metadata": {"priority": "high"},  # dict → TEXT (stored as JSON)
        },
        # Add more example records with same structure...
    ]

    # Handle duplicates properly
    if existing_table:
        existing_ids = {row["id"] for row in existing_table.rows}
        raw_data = [record for record in raw_data if record["id"] not in existing_ids]
        print(f"{{ resource_name }}: Adding {len(raw_data)} new records")

    return raw_data
{% else %}
    # TODO: Implement your data fetching logic
    # This is just an example - replace with your actual schema and data
    return [
        {
            "id": 1,  # Required: some kind of identifier
            "title": "Example Document",  # Your field names and types
            "content": "Document content...",  # You decide what goes in main vs fragments
            # Add any other fields your project needs
        },
        # Add more records...
    ]
{% endif %}


{% if fragments %}
{{ 'async ' if is_async else '' }}def fetch_fragments_data(existing_fragments_table: Optional[Table], main_data_context: Optional[List[Dict[str, Any]]] = None) -> List[Dict[str, Any]]:
    """
    {{ 'Async f' if is_async else 'F' }}etch fragments data for the {{ resource_name }}_fragments table.

    This is called automatically after fetch_data().

    Args:
        existing_fragments_table: sqlite-utils Table object if exists, None for new table
                                 Use this to check existing fragments and avoid duplicates
        main_data_context: Raw data from fetch_data() to avoid duplicate API calls (optional)
                          Contains the same data returned by your fetch_data() function

    Returns:
        List[Dict[str, Any]]: Fragment records with YOUR chosen schema

    IMPORTANT: You have complete freedom to define the fragments schema.
    Common patterns include:

    1. Simple text chunks:
       {"parent_id": 1, "text": "fragment content"}

    2. Positional fragments:
       {"doc_id": 1, "position": 0, "content": "...", "length": 500}

    3. Semantic fragments:
       {"document_id": 1, "section_type": "intro", "text": "...", "page": 1}

    4. Custom fragments:
       {"source_id": 1, "fragment_data": "...", "metadata": {"type": "citation"}}

    The only requirement: some way to link fragments back to main records.
    """
    # TODO: Implement your {{ 'async ' if is_async else '' }}fragments logic
    # This is just an example - replace with your actual implementation

    # OPTION 1: Use main_data_context to avoid duplicate API calls
    if main_data_context:
        fragments = []
        for main_record in main_data_context:
            # Use data already fetched in fetch_data() - no duplicate API calls!
            doc_content = main_record.get("content", "")  # Or however you store content
            doc_id = main_record.get("id")  # Or your identifier field

{% if is_async %}
            # Async text processing (e.g., AI-based chunking)
            chunks = await async_split_content(doc_content)
{% else %}
            # Split the content into fragments
            chunks = doc_content.split("\n\n")  # Or your preferred splitting logic
{% endif %}

            for i, chunk in enumerate(chunks):
                if chunk.strip():
                    fragments.append(
                        {
                            "parent_id": doc_id,  # Link to main table record
                            "fragment_num": i,  # Fragment ordering
                            "text": chunk.strip(),  # Fragment content
                            "char_count": len(chunk),  # Your metadata
                        }
                    )
        return fragments

    # OPTION 2: Fallback to independent {{ 'async ' if is_async else '' }}data fetch (for backward compatibility)
    # This runs when main_data_context is None (e.g., testing, old resources)
    example_text = """
    This is an example document that will be split into fragments.
    You can implement any splitting logic you need.

    Maybe you want sentence-based fragments, paragraph-based,
    or even semantic chunks based on document structure.

    The choice is entirely yours based on your project needs.
    """

{% if is_async %}
    # Your async splitting logic goes here
    fragments = []
    chunks = await async_split_content(example_text)

    for i, chunk in enumerate(chunks):
        if chunk.strip():
            fragments.append(
                {
                    "parent_id": 1,  # Link to main table (your choice of field name)
                    "sequence": i,  # Your choice of ordering
                    "text": chunk.strip(),  # Your choice of content field name
                    # Add any other fields your fragments need
                }
            )
{% else %}
    # Your splitting logic goes here
    fragments = []
    sentences = example_text.split(".")
    for i, sentence in enumerate(sentences):
        if sentence.strip():
            fragments.append(
                {
                    "parent_id": 1,  # Link to main table (your choice of field name)
                    "sequence": i,  # Your choice of ordering
                    "text": sentence.strip(),  # Your choice of content field name
                    # Add any other fields your fragments need
                }
            )
{% endif %}

    return fragments

{% endif %}

{{ 'async ' if is_async else '' }}def transform_data(raw_data: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
    """
    Optional: Transform{{ '/clean the raw' if not fragments else ' main table' }} data before database insertion.

    Args:
        raw_data: The data returned from fetch_data()

    Returns:
        List[Dict[str, Any]]: Transformed data
{% if not fragments %}

    Examples:
        # Clean strings
        for item in raw_data:
            item['name'] = item['name'].strip().title()

        # Parse dates
        for item in raw_data:
            item['created_date'] = datetime.fromisoformat(item['date_string'])

        # Handle complex data (sqlite-utils stores as JSON)
        for item in raw_data:
            item['metadata'] = {"tags": ["news", "tech"], "priority": 1}
{% endif %}
    """
    # TODO: Add any {{ 'async ' if is_async else '' }}data transformation logic here
{% if is_async %}
    await asyncio.sleep(0)  # Placeholder for async operations
{% endif %}
    return raw_data


{% if fragments %}
{{ 'async ' if is_async else '' }}def transform_fragments_data(raw_fragments: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
    """
    Optional: Transform fragment data before database insertion.

    Args:
        raw_fragments: The data returned from fetch_fragments_data()

    Returns:
        List[Dict[str, Any]]: Transformed fragment data
    """
    # TODO: Add any {{ 'async ' if is_async else '' }}fragment processing logic here
{% if is_async %}
    await asyncio.sleep(0)  # Placeholder for async operations
{% endif %}
    return raw_fragments


{% endif %}
{% if is_async and not fragments %}
# TODO: Add any helper functions your project needs
# Examples:
# - Async API client functions
# - Async data parsing utilities
# - Async validation functions
# - Custom async data transformation functions

async def fetch_from_api():
    """Example async helper function."""
    async with aiohttp.ClientSession() as session:
        async with session.get("https://api.example.com/data") as response:
            return await response.json()

async def fetch_initial_data():
    """Example async helper for initial data fetch."""
    # Your initial data fetching logic here
    await asyncio.sleep(0.1)  # Simulate async operation
    return [
        {"id": 1, "name": "Initial Record", "created": "2024-01-01"},
        {"id": 2, "name": "Another Record", "created": "2024-01-02"},
    ]
{% elif is_async and fragments %}
# TODO: Add any async helper functions your project needs
# Examples:
# - Async document parsing functions
# - Async text splitting utilities
# - Async data validation functions
# - Async API client functions

async def async_split_content(content: str) -> List[str]:
    """Example async text splitting function."""
    # Simulate async processing (e.g., AI-based chunking)
    await asyncio.sleep(0.1)

    # Simple paragraph-based splitting for example
    paragraphs = content.split('\n\n')
    return [p.strip() for p in paragraphs if p.strip()]

async def async_fetch_documents():
    """Example async document fetching."""
    async with aiohttp.ClientSession() as session:
        async with session.get("https://api.example.com/documents") as response:
            return await response.json()

async def async_process_document(doc_content: str) -> List[str]:
    """Example async document processing."""
    # Could call AI services, external APIs, etc.
    await asyncio.sleep(0.1)
    return doc_content.split('\n\n')
{% elif not is_async and not fragments %}
# TODO: Add any helper functions your project needs
# Examples:
# - API client functions
# - Data parsing utilities
# - Validation functions
# - Custom data transformation functions
{% else %}
# TODO: Add any helper functions your project needs
# Examples:
# - Document parsing functions
# - Text splitting utilities
# - Data validation functions
# - API client functions
{% endif %}
