"""
GraphQL resolvers for Information Spaces.

Implements resolver functions for space queries and mutations.
"""

from typing import Optional, Dict, Any, List
from pathlib import Path

from .space_schema import (
    InformationSpaceType,
    SpaceDocumentType,
    SpaceVariableType,
    SpaceConfigType,
    SpaceMetadataType,
    RenderResultType,
    SyncResultType,
    SyncConflictType,
    CreateSpacePayload,
    UpdateSpacePayload,
    DeleteSpacePayload,
    AddDocumentPayload,
    RemoveDocumentPayload,
    SetVariablePayload,
    RenderDocumentPayload,
    SyncSpacePayload,
)


def get_space_service():
    """Get the space service instance."""
    # This would be configured during app initialization
    # For now, we create a new instance
    from ..spaces.services.space_service import SpaceService
    from ..spaces.repositories.sqlite import (
        SqliteSpaceRepository,
        SqliteDocumentRepository,
        SqliteVariableRepository,
        SqliteReferenceRepository,
    )

    # Use default database path
    import os
    db_path = Path(os.path.expanduser("~/.markitect/spaces.db"))
    db_path.parent.mkdir(parents=True, exist_ok=True)

    return SpaceService(
        space_repo=SqliteSpaceRepository(db_path),
        document_repo=SqliteDocumentRepository(db_path),
        variable_repo=SqliteVariableRepository(db_path),
        reference_repo=SqliteReferenceRepository(db_path),
    )


def _space_to_graphql(space) -> InformationSpaceType:
    """Convert a space model to GraphQL type."""
    config = None
    if space.config:
        config = SpaceConfigType(
            default_variant=space.config.default_variant,
            enable_caching=space.config.enable_caching,
            theme=space.config.theme,
            history_enabled=space.config.history_enabled,
            variable_scope=space.config.variable_scope,
        )

    metadata = None
    if space.metadata:
        metadata = SpaceMetadataType(
            tags=space.metadata.tags if hasattr(space.metadata, 'tags') else [],
            author=space.metadata.author if hasattr(space.metadata, 'author') else None,
            custom=space.metadata.custom if hasattr(space.metadata, 'custom') else {},
        )

    return InformationSpaceType(
        id=space.id,
        name=space.name,
        description=space.description,
        status=space.status.value if hasattr(space.status, 'value') else str(space.status),
        config=config,
        metadata=metadata,
        parent_space_id=space.parent_space_id,
        created_at=space.created_at,
        updated_at=space.updated_at,
    )


def _document_to_graphql(doc) -> SpaceDocumentType:
    """Convert a document model to GraphQL type."""
    return SpaceDocumentType(
        id=doc.id,
        space_id=doc.space_id,
        document_id=doc.document_id,
        space_path=doc.space_path,
        order_index=doc.order_index,
        metadata=doc.metadata,
        added_at=doc.added_at,
    )


def _variable_to_graphql(var) -> SpaceVariableType:
    """Convert a variable model to GraphQL type."""
    return SpaceVariableType(
        space_id=var.space_id,
        name=var.name,
        value=var.value,
        scope=var.scope,
    )


# Query Resolvers
def resolve_space(root, info, id=None, name=None):
    """Resolve a single space by ID or name."""
    service = get_space_service()

    if id:
        space = service.get_space(id)
    elif name:
        space = service.get_space_by_name(name)
    else:
        return None

    if space:
        return _space_to_graphql(space)
    return None


def resolve_spaces(root, info, status=None, limit=50, offset=0):
    """Resolve list of spaces."""
    service = get_space_service()
    spaces = service.list_spaces(status=status, limit=limit, offset=offset)
    return [_space_to_graphql(s) for s in spaces]


def resolve_space_documents(root, info, space_id):
    """Resolve documents in a space."""
    service = get_space_service()
    documents = service.list_documents(space_id)
    return [_document_to_graphql(d) for d in documents]


def resolve_space_document(root, info, space_id, space_path):
    """Resolve a specific document in a space."""
    service = get_space_service()
    doc = service.get_document(space_id, space_path)
    if doc:
        return _document_to_graphql(doc)
    return None


def resolve_space_variables(root, info, space_id, scope=None):
    """Resolve variables in a space."""
    service = get_space_service()
    variables = service.list_variables(space_id, scope=scope)
    return [_variable_to_graphql(v) for v in variables]


def resolve_space_variable(root, info, space_id, name):
    """Resolve a specific variable."""
    service = get_space_service()
    var = service.get_variable(space_id, name)
    if var:
        return _variable_to_graphql(var)
    return None


# Mutation Resolvers
def resolve_create_space(root, info, input):
    """Create a new space."""
    try:
        service = get_space_service()

        # Build config and metadata from input
        from ..spaces.models import SpaceConfig, SpaceMetadata

        config = SpaceConfig()
        if input.config:
            if input.config.default_variant:
                config.default_variant = input.config.default_variant
            if input.config.enable_caching is not None:
                config.enable_caching = input.config.enable_caching
            if input.config.theme:
                config.theme = input.config.theme
            if input.config.history_enabled is not None:
                config.history_enabled = input.config.history_enabled

        metadata = SpaceMetadata()
        if input.metadata:
            if input.metadata.tags:
                metadata.tags = input.metadata.tags
            if input.metadata.author:
                metadata.author = input.metadata.author
            if input.metadata.custom:
                metadata.custom = input.metadata.custom

        space = service.create_space(
            name=input.name,
            description=input.description,
            config=config,
            metadata=metadata,
            parent_space_id=input.parent_space_id,
        )

        return CreateSpacePayload(
            space=_space_to_graphql(space),
            success=True,
            errors=[],
        )

    except Exception as e:
        return CreateSpacePayload(
            space=None,
            success=False,
            errors=[str(e)],
        )


def resolve_update_space(root, info, id, input):
    """Update a space."""
    try:
        service = get_space_service()

        updates = {}
        if input.name:
            updates["name"] = input.name
        if input.description:
            updates["description"] = input.description

        space = service.update_space(id, **updates)

        return UpdateSpacePayload(
            space=_space_to_graphql(space),
            success=True,
            errors=[],
        )

    except Exception as e:
        return UpdateSpacePayload(
            space=None,
            success=False,
            errors=[str(e)],
        )


def resolve_delete_space(root, info, id):
    """Delete a space."""
    try:
        service = get_space_service()
        service.delete_space(id)

        return DeleteSpacePayload(
            success=True,
            deleted_id=id,
            errors=[],
        )

    except Exception as e:
        return DeleteSpacePayload(
            success=False,
            deleted_id=None,
            errors=[str(e)],
        )


def resolve_activate_space(root, info, id):
    """Activate a draft space."""
    try:
        service = get_space_service()
        space = service.activate_space(id)

        return UpdateSpacePayload(
            space=_space_to_graphql(space),
            success=True,
            errors=[],
        )

    except Exception as e:
        return UpdateSpacePayload(
            space=None,
            success=False,
            errors=[str(e)],
        )


def resolve_archive_space(root, info, id):
    """Archive a space."""
    try:
        service = get_space_service()
        space = service.archive_space(id)

        return UpdateSpacePayload(
            space=_space_to_graphql(space),
            success=True,
            errors=[],
        )

    except Exception as e:
        return UpdateSpacePayload(
            space=None,
            success=False,
            errors=[str(e)],
        )


def resolve_add_document(root, info, space_id, input):
    """Add a document to a space."""
    try:
        service = get_space_service()

        doc = service.add_document(
            space_id=space_id,
            document_id=input.document_id,
            space_path=input.space_path,
            order_index=input.order_index or 0,
            metadata=input.metadata,
        )

        return AddDocumentPayload(
            document=_document_to_graphql(doc),
            success=True,
            errors=[],
        )

    except Exception as e:
        return AddDocumentPayload(
            document=None,
            success=False,
            errors=[str(e)],
        )


def resolve_remove_document(root, info, space_id, space_path):
    """Remove a document from a space."""
    try:
        service = get_space_service()
        service.remove_document(space_id, space_path)

        return RemoveDocumentPayload(
            success=True,
            removed_path=space_path,
            errors=[],
        )

    except Exception as e:
        return RemoveDocumentPayload(
            success=False,
            removed_path=None,
            errors=[str(e)],
        )


def resolve_set_variable(root, info, space_id, name, value, scope=None):
    """Set a variable in a space."""
    try:
        service = get_space_service()

        var = service.set_variable(
            space_id=space_id,
            name=name,
            value=value,
            scope=scope or "space",
        )

        return SetVariablePayload(
            variable=_variable_to_graphql(var),
            success=True,
            errors=[],
        )

    except Exception as e:
        return SetVariablePayload(
            variable=None,
            success=False,
            errors=[str(e)],
        )


def resolve_delete_variable(root, info, space_id, name):
    """Delete a variable from a space."""
    try:
        service = get_space_service()
        service.delete_variable(space_id, name)

        return SetVariablePayload(
            variable=None,
            success=True,
            errors=[],
        )

    except Exception as e:
        return SetVariablePayload(
            variable=None,
            success=False,
            errors=[str(e)],
        )


def resolve_render_document(root, info, space_id, document_path, options=None):
    """Render a document to HTML."""
    try:
        from ..spaces.rendering import SpaceRenderingService, RenderConfig, ThemeConfig

        service = get_space_service()
        doc = service.get_document(space_id, document_path)

        if not doc:
            return RenderDocumentPayload(
                result=None,
                success=False,
                errors=[f"Document not found: {document_path}"],
            )

        # Get render config from options
        theme_name = "default"
        include_toc = False
        force_refresh = False

        if options:
            if options.theme:
                theme_name = options.theme
            if options.include_toc:
                include_toc = options.include_toc
            if options.force_refresh:
                force_refresh = options.force_refresh

        config = RenderConfig(
            theme=ThemeConfig(name=theme_name, layers=[theme_name]),
            include_toc=include_toc,
        )

        renderer = SpaceRenderingService()
        # Would need actual content here
        result = renderer.render_document(
            content="# Placeholder",  # Would get actual content
            document_id=doc.document_id,
            space_id=space_id,
            force_refresh=force_refresh,
        )

        return RenderDocumentPayload(
            result=RenderResultType(
                content=result.content,
                format=result.format.value,
                content_hash=result.content_hash,
                source_hash=result.source_hash,
                document_id=result.document_id,
                cached=False,
            ),
            success=True,
            errors=[],
        )

    except Exception as e:
        return RenderDocumentPayload(
            result=None,
            success=False,
            errors=[str(e)],
        )


def resolve_sync_space(root, info, space_id, directory, options=None):
    """Sync a space with a directory."""
    try:
        from ..spaces.sync import (
            BidirectionalSyncCoordinator,
            SyncConfig,
            SyncDirection,
            ConflictResolution,
        )

        service = get_space_service()
        space = service.get_space(space_id)

        if not space:
            return SyncSpacePayload(
                result=None,
                conflicts=[],
                success=False,
                errors=[f"Space not found: {space_id}"],
            )

        # Build sync config
        direction = SyncDirection.BIDIRECTIONAL
        conflict_res = ConflictResolution.NEWER_WINS
        dry_run = False

        if options:
            if options.direction:
                direction = SyncDirection(options.direction)
            if options.conflict_resolution:
                conflict_res = ConflictResolution(options.conflict_resolution)
            if options.dry_run:
                dry_run = options.dry_run

        config = SyncConfig(
            direction=direction,
            conflict_resolution=conflict_res,
            dry_run=dry_run,
        )

        coordinator = BidirectionalSyncCoordinator(config)

        documents = service.list_documents(space_id)

        def content_provider(doc_id):
            return ""  # Would get actual content

        result = coordinator.sync(
            space=space,
            documents=documents,
            content_provider=content_provider,
            directory=Path(directory),
        )

        return SyncSpacePayload(
            result=SyncResultType(
                success=result.success,
                created_count=result.created_count,
                updated_count=result.updated_count,
                deleted_count=result.deleted_count,
                conflict_count=len(result.conflicts),
                errors=list(result.errors.values()),
            ),
            conflicts=[
                SyncConflictType(
                    path=c.path,
                    space_hash=c.space_state.content_hash if c.space_state else None,
                    directory_hash=c.directory_state.content_hash if c.directory_state else None,
                    resolution=c.resolution.value if hasattr(c.resolution, 'value') else str(c.resolution),
                    winner=c.winner,
                )
                for c in result.conflicts
            ],
            success=result.success,
            errors=[],
        )

    except Exception as e:
        return SyncSpacePayload(
            result=None,
            conflicts=[],
            success=False,
            errors=[str(e)],
        )


def resolve_invalidate_cache(root, info, space_id, document_path=None):
    """Invalidate render cache."""
    try:
        from ..spaces.rendering import SpaceRenderingService

        service = SpaceRenderingService()

        if document_path:
            service.invalidate_document(document_path, space_id)
        else:
            service.invalidate_space(space_id)

        return True

    except Exception:
        return False
