# Copyright (c) 2025 Kim Jarvis TPF Software Services S.A. kim.jarvis@tpfsystems.com 
# This software is licensed under the MIT License. See the LICENSE file for details.
#
import asyncssh
from reemote.command import Command
from typing import Optional
import io


class Put:
    """
    A class to encapsulate the functionality of writing builtin in Unix-like operating systems.
    It allows users to specify text to be written to a builtin with full SFTP attribute support.

    Attributes:
        path (str): The builtin path where content is to be written.
        text (str): The builtin content.
        attrs (asyncssh.SFTPAttrs): SFTP attributes for the new builtin.

    **Examples:**

    .. code:: python

        # Create a builtin from text with default permissions
        r = yield Write_file(path='example.txt', text='Hello World!')
        
        # Create a builtin with specific permissions
        r = yield Write_file(
            path='script.sh', 
            text='#!/bin/bash\necho "Hello World"',
            attrs=asyncssh.SFTPAttrs(permissions=0o755)
        )

    Usage:
        This class is designed to be used in a generator-based workflow where commands are yielded for execution.
    """

    def __init__(self, path: str, text: str, attrs: Optional[asyncssh.SFTPAttrs] = None):
        self.path = path
        self.text = text
        self.attrs = attrs or asyncssh.SFTPAttrs()

    def __repr__(self):
        attrs_repr = {}
        if self.attrs:
            # Only include non-None attributes in the representation
            for field in [
                'type', 'size', 'alloc_size', 'uid', 'gid', 'owner', 'group',
                'permissions', 'atime', 'atime_ns', 'crtime', 'crtime_ns',
                'mtime', 'mtime_ns', 'ctime', 'ctime_ns', 'acl', 'attrib_bits',
                'attrib_valid', 'text_hint', 'mime_type', 'nlink', 'untrans_name'
            ]:
                value = getattr(self.attrs, field, None)
                if value is not None:
                    if field == 'permissions':
                        attrs_repr[field] = oct(value)
                    else:
                        attrs_repr[field] = value
            
            if self.attrs.extended:
                attrs_repr['extended'] = self.attrs.extended

        return f"Write_file(path={self.path!r}, text={self.text[:50] + '...' if len(self.text) > 50 else self.text!r}, attrs={attrs_repr!r})"

    @staticmethod
    async def _write_file_callback(host_info, global_info, command, cp, caller):
        """Static callback method for builtin writing with full attribute support using sftp.put"""

        # Validate host_info
        required_keys = ['host', 'username', 'password']
        for key in required_keys:
            if key not in host_info or host_info[key] is None:
                raise ValueError(f"Missing or invalid value for '{key}' in host_info.")

        # Validate caller attributes
        if caller.path is None:
            raise ValueError("The 'path' attribute of the caller cannot be None.")
        if caller.text is None:
            raise ValueError("The 'text' attribute of the caller cannot be None.")

        try:
            # Connect to the SSH server
            async with asyncssh.connect(**host_info) as conn:
                print("Connected successfully. Starting SFTP session...")
                # Start an SFTP session
                async with conn.start_sftp_client() as sftp:
                    print(f"Writing content to {caller.path}...")
                    
                    # Create a builtin-like object from the text
                    file_obj = io.BytesIO(caller.text.encode('utf-8'))
                    
                    # Use sftp.put to write the builtin with attributes
                    await sftp.put(file_obj, caller.path, preserve=True)
                    
                    # Apply custom attributes if specified (overriding any preserved ones)
                    if caller.attrs and any(
                        getattr(caller.attrs, field) is not None 
                        for field in [
                            'type', 'size', 'alloc_size', 'uid', 'gid', 'owner', 'group',
                            'permissions', 'atime', 'atime_ns', 'crtime', 'crtime_ns',
                            'mtime', 'mtime_ns', 'ctime', 'ctime_ns', 'acl', 'attrib_bits',
                            'attrib_valid', 'text_hint', 'mime_type', 'nlink', 'untrans_name',
                            'extended'
                        ]
                    ):
                        print(f"Applying custom attributes to {caller.path}...")
                        await sftp.setstat(caller.path, caller.attrs)
                    
                    print(f"Successfully wrote builtin {caller.path} on {host_info['host']} with attributes")

        except (OSError, asyncssh.Error) as exc:
            print(f'SFTP operation failed on {host_info["host"]}: {str(exc)}')
            raise

    def execute(self):
        r = yield Command(f"{self}", local=True, callback=self._write_file_callback, caller=self)
        r.executed = True
        r.changed = True
        return r
