
    k)iW                        d Z ddlZddlZddlZddlZddlZddlmZ ddlmZ ddl	m
Z
mZmZmZ ddlZddlmZ ddlmZmZ ddlmZ dd	lmZ dd
lmZ ddlmZ ddlmZ ddlmZ ddl m!Z!  ejD                  e#      Z$dFdZ%de&ddfdZ'de(dede)fdZ*ejV                  fde&dede&fdZ,de(de&fdZ-dGde&de&fdZ.de&de/e&e&e&f   fdZ0de1ddfdZ2de&de&de/e&e&f   fd Z3 ed!      d"d#fd$ed%ede1de&dee
   f
d&Z4de1d'e&d(e&de5fd)Z6 ed!      d#fd$ed*ee   de&dee
   fd+Z7de&ddfd,Z8d- Z9de&de&fd.Z:	 	 dHd/e1d'e&de&d0ee&   de5f
d1Z;d/e1de&de5fd2Z<	 dGde&d0ee&   dee1   fd3Z=de&dee1   fd4Z>d5e&de&fd6Z?d5e&dee1   fd7Z@d8e&d$edee&   fd9ZAd:e1deee(e(f      fd;ZBd:e1deee(e(f      fd<ZCd:e1deee(e(f      fd=ZDd:e1deee(e(f      fd>ZEd:e1d?e(deee(e(f      fd@ZFde
e&e)f   fdAZGdBe1de
e&e)f   fdCZHdDe
e&e)f   ddfdEZIy)Iz
File I/O operations module.

This module handles file upload, download, and management operations
for both local filesystem and AWS S3 storage.
    N)datetime)Path)DictListOptionalTuple)ClientError)File
UploadFile)Session)get_active_cdn)settings)APIException)MAX_UPLOAD_SIZE_MB)FileSizeUnits)generate_secure_idreturnc                     	 t        j                  t        j                  d       t        j                  dt        j                          y# t        $ r1} dt        j                   d|  }t        j                  |        d} ~ ww xY w)z
    Create the main uploads directory if it doesn't exist.
    
    Raises:
        Exception: If directory creation fails.
    T)exist_okz0Uploads directory created or already exists at: z&Failed to create uploads directory at 	 due to: N)osmakedirsr   UPLOADS_DIRloggerinfo	Exceptionerror)eerr_msgs     V/var/www/html/hwPaymentPortal-be-dev/hw-payment-portal-api/src/apps/files/helper/io.pycreate_uploads_directoryr!       su    
H((48FxG[G[F\]^ 4X5I5I4J)TUSVW 	 	Ws   AA 	B,BBpathc                     	 t                t        |       j                  }|j                  dd       t        j                  d|        y# t        $ r#}d|  d| }t        j                  |        d}~ww xY w)z
    Create nested directory structure for file uploads.
    
    Args:
        path: The file path for which to create parent directories.
        
    Raises:
        Exception: If directory creation fails.
    T)parentsr   z/Nested directory created or already exists at: z&Failed to create nested directory for r   N)r!   r   parentmkdirr   r   r   r   )r"   nested_pathr   r   s       r    create_nested_upload_directoryr(   2   st     "4j''$6Ek]ST :4&	!MWs   A
A 	A9A44A9size_in_bytesunitc                     t         j                  dt         j                  dt         j                  di}|j	                  |d      }| |z  S )z
    Convert file size from bytes to specified unit.
    
    Args:
        size_in_bytes: Size in bytes to convert.
        unit: Target unit for conversion.
        
    Returns:
        Converted size as float.
             @   )r   KBMBGBget)r)   r*   conversion_factorsfactors       r    convert_filesizer6   G   sM     	$+,  ##D!,F6!!    filepathc                    	 t         j                  j                  t        j                         t        j
                  |       }t         j                  j                  |      }t        ||      }t        |d      r|j                  j                         nd}|d| S # t        $ r%}t        j                  d|  d|        Y d}~yd}~ww xY w)	z
    Get the physical size of a file on the local filesystem.
    
    Args:
        filepath: Relative path to the file.
        unit: Unit for size measurement.
        
    Returns:
        File size as formatted string with unit.
    r*   valuemb.2fzError getting file size for : N0kb)r   r"   joingetcwdr   r   getsizer6   hasattrr;   lowerr   r   r   )r8   r*   	file_pathsizeconverted_sizeunit_suffixr   s          r    get_physical_filesizerI   \   s    GGLLh.B.BHM	wwy))$T:,3D',Bdjj&&( %k]33 3H:RsCDs   BB 	C'CCrF   c                 `    | dk  r|  dS | dk  r	| dz  ddS | dk  r	| dz  ddS | dz  ddS )	z
    Format file size with appropriate unit.
    
    Args:
        size: Size in bytes.
        
    Returns:
        Formatted size string with appropriate unit.
    r,   z bytesr-   r=   z KBr.   z MBz GB )rF   s    r    _format_file_sizerL   r   se     d{v		t)C$$	"	"	"3's++',C00r7   c                    	 |r*|j                   j                         dk(  rt        |       }nt        |       }|syt	        t        |            S # t        $ r%}t        j                  d|  d|        Y d}~yd}~ww xY w)z
    Get approximate file size by downloading and measuring content.
    
    Args:
        filepath: Path to the file.
        active_cdn: Active CDN configuration.
        
    Returns:
        Formatted file size string.
    s3r"   z0 bytesz(Error getting approximate file size for r>   N)	labelrD   download_file_from_s3download_file_from_system_pathrL   lenr   r   r   )r8   
active_cdndownloaded_file_bytesr   s       r    get_approximate_filesizerV      sz    ***002d:$9x$H!$B$Q!$ %:!;<< ?zA3OPs   :A A 	A?A::A?original_filenamec                    | j                  dd      }|j                  dd      }t        |      dk(  r|\  }}n|d   d}}t        t	        j
                         j                                }t        d	      }| d
| d| }|||fS )z
    Generate a unique filename with timestamp and secure ID.
    
    Args:
        original_filename: Original filename from upload.
        
    Returns:
        Tuple of (unique_filename, basename, file_format).
     -.r/      r   binfile)prepend_)replacersplitrS   intr   utcnow	timestampr   )rW   cleaned_name
name_partsbasenamefile_formatsuffix	unique_idunique_filenames           r    _generate_unique_filenamerm      s     %,,S#6L$$S!,J
:! *+ *1u+HOO%//123F"62I"1VHAk];OHk11r7   contentsc                 h    t         dz  }t        |       |kD  rt        t        i ddt          d      y)z
    Validate that file size doesn't exceed maximum allowed size.
    
    Args:
        contents: File content as bytes.
        
    Raises:
        APIException: If file size exceeds maximum allowed size.
    i@B   z#File exceeds maximum size limit of r<   moduler   status_codemessageN)r   rS   r   __name__)rn   max_size_bytess     r    _validate_file_sizerw      sC     ('1N
8}~%9:L9MRP	
 	
 &r7   filenamec                     | r| j                   n%t        j                          t        j                   }d| | }||z   }||fS )a  
    Determine upload and serve paths based on CDN configuration.
    
    Args:
        active_cdn: Active CDN configuration.
        path: Upload path prefix.
        filename: Generated filename.
        
    Returns:
        Tuple of (serve_path, upload_path).
    /)upload_pathr   rA   r   r   )rT   r"   rx   	base_path
serve_pathr{   s         r    _determine_upload_pathsr~      sT     #-
		}X1123  TF8*%Jj(K{""r7   .r7    dbr^   c                 B   t        |       t        |j                        \  }}}|j                  }t	        |       }t        |||      \  }	}
t        ||||
      }|st        t        i dd      t        t        |      t        j                        }||||	||dddS )	a  
    Upload a single file to configured storage (local or S3).
    
    Args:
        db: Database session.
        file: FastAPI UploadFile object.
        contents: File contents as bytes.
        path: Upload path prefix.
        
    Returns:
        Dictionary containing file metadata or None if upload fails.
        
    Raises:
        APIException: If file upload fails.
    r   rp   zCould not upload file.rq   r:   r=   r<   )	file_nameoriginal_nameformatr"   content_typefilesize)rw   rm   rx   r   r   r~   _perform_uploadr   ru   r6   rS   r   r1   )r   r^   rn   r"   r   rh   ri   r   rT   r}   r{   uploadedfile_size_mbs                r    upload_file_singler      s    * !'@'O$Ix$$L2&J5j$	RJz8\;OH,	
 	
 $CM8H8HIL !$#C(+ r7   r   r{   c                 v    | r+| j                   j                         dk(  rt        |||      S t        ||      S )aI  
    Perform the actual file upload based on CDN configuration.
    
    Args:
        active_cdn: Active CDN configuration.
        contents: File contents as bytes.
        content_type: MIME type of the file.
        upload_path: Full upload path.
        
    Returns:
        True if upload successful, False otherwise.
    rN   )file_contentr   r"   )r   r"   )rP   rD   upload_file_to_s3upload_file_to_system_path)rT   rn   r   r{   s       r    r   r     sB     j&&,,.$6 !%
 	
 *xkRRr7   filesc                     g }|D ]F  }|j                   j                         }t        |       t        | |||      }|j	                  |       H |S )a`  
    Upload multiple files to configured storage.
    
    Args:
        db: Database session.
        files: List of FastAPI UploadFile objects.
        path: Upload path prefix.
        
    Returns:
        List of dictionaries containing file metadata.
        
    Raises:
        APIException: If any file upload fails or exceeds size limit.
    )r^   readrw   r   append)r   r   r"   uploaded_filesr^   rn   uploaded_files          r    upload_file_multipler   -  sV    & N -99>>#H%*2tXtDm,- r7   c                    	 t        j                  |        t        j                  d|         y# t        $ r t        j                  d|         Y yt        $ r%}t        j                  d|  d|        Y d}~yd}~ww xY w)zw
    Remove a single file from the local filesystem.
    
    Args:
        path: Full path to the file to remove.
    zSuccessfully removed file: zFile not found for removal: zFailed to remove file r>   N)r   unlinkr   r   FileNotFoundErrorwarningr   r   )r"   r   s     r    remove_file_singler   L  so    ;
		$1$89 >5dV<= ;-dV2aS9::;s   -0 !B B A;;B c                  l    t        j                  dt        j                  t        j                  d      S )ze
    Create and return configured S3 client.
    
    Returns:
        Boto3 S3 client instance.
    rN   z	us-east-1)service_nameaws_access_key_idaws_secret_access_keyregion_name)boto3clientr   AWS_S3_ACCESS_KEYAWS_S3_SECRET_KEYrK   r7   r    _create_s3_clientr   \  s-     <<"44&88	 r7   c                 2    | j                  d      r| dd S | S )z
    Remove leading slash from S3 path if present.
    
    Args:
        path: Original path.
        
    Returns:
        Sanitized path without leading slash.
    rz   r/   N)
startswithrO   s    r    _sanitize_s3_pathr   k  s      s+4855r7   r   bucketc                    |t         j                  }	 t               }t        |      }|j	                  t        j                  |       ||d|i       t        j                  d|        y# t        $ r"}t        j                  d|        Y d}~yd}~wt        $ r"}t        j                  d|        Y d}~yd}~ww xY w)	aC  
    Upload a file to AWS S3 bucket.
    
    Args:
        file_content: File content as bytes.
        content_type: MIME type of the file.
        path: S3 object key/path.
        bucket: S3 bucket name. If None, uses default from settings.
        
    Returns:
        True if upload successful, False otherwise.
    NContentType)FileobjBucketKey	ExtraArgsz"Successfully uploaded file to S3: TzAWS S3 upload error: Fz#Unexpected error during S3 upload: )r   AWS_S3_DEFAULT_BUCKETr   r   upload_fileobjioBytesIOr   r   r	   r   r   )r   r   r"   r   	s3_clientsanitized_pathr   s          r    r   r   x  s    $ ~//%'	*40  JJ|,$l3	 	! 	
 	88HIJ ,QC01 :1#>?s$   AA, ,	C 5BC B;;C c                 :   	 t         j                  d|        |rt        |       t        |d      5 }|j	                  |        ddd       t         j                  d       y# 1 sw Y   xY w# t
        $ r"}t         j                  d|        Y d}~yd}~ww xY w)z
    Upload a file to the local filesystem.
    
    Args:
        file_content: File content as bytes.
        path: Full filesystem path where file should be saved.
        
    Returns:
        True if upload successful, False otherwise.
    zAttempting to upload to path: wbNz.File uploaded successfully to local filesystemTzLocal file upload error: F)r   r   r(   openwriter   r   )r   r"   fr   s       r    r   r     s    4TF;<*40$ 	"GGL!	" 	DE		" 	"
  045s.   1A/ A#A/ #A,(A/ /	B8BBc                 R   	 t               }t        |       }|t        j                  }t	        j
                         }|j                  |||       t        j                  d|        |j                         S # t        $ r%}t        j                  d|  d|        Y d}~yd}~ww xY w)z
    Download a file from AWS S3 bucket.
    
    Args:
        path: S3 object key/path.
        bucket: S3 bucket name.
        
    Returns:
        File content as bytes, or None if download fails.
    Nz&Successfully downloaded file from S3: zCould not fetch file from S3 r>   )r   r   r   r   r   r   download_fileobjr   r   getvaluer   r   )r"   r   r   r   bufferr   s         r    rQ   rQ     s    %'	*40>33F""6>6B<^<LMN   4TF"QC@As   A5A8 8	B&B!!B&c                 @   	 t        | d      5 }|j                         }ddd       t        j                  d|         S # 1 sw Y   #xY w# t        $ r t        j                  d|         Y yt        $ r%}t        j                  d|  d|        Y d}~yd}~ww xY w)z
    Download a file from the local filesystem.
    
    Args:
        path: Full filesystem path to the file.
        
    Returns:
        File content as bytes, or None if download fails.
    rbNz.Successfully downloaded file from filesystem: zFile not found at z&Could not fetch file from system path r>   )r   r   r   r   r   r   r   )r"   r   datar   s       r    rR   rR     s    $ 	668D	 	DTFKL		 	
  )$01 =dV2aSIJs3   A A!A A
A !B0B8BB
image_pathc                     | }|j                   |j                  |j                  fD ]  }|s||v s|j                  |d      } |S )z
    Clean image path by removing CDN-specific prefixes.
    
    Args:
        image_path: Original image path.
        active_cdn: Active CDN configuration.
        
    Returns:
        Cleaned image path.
    r   )hostrootr"   ra   )r   rT   cleaned_pathpath_prefixs       r    _clean_image_pathr     sQ     L"*//J A;,6'//R@LA r7   c                     | j                    | }| j                  j                         dk(  rt        |      S t	        |      S )z
    Download image content based on CDN configuration.
    
    Args:
        active_cdn: Active CDN configuration.
        image_path: Path to the image.
        
    Returns:
        Image content as bytes, or None if download fails.
    rN   rO   )r{   rP   rD   rQ   rR   )rT   r   	full_paths      r    _download_image_contentr     sF     ))*:,7I4'$)44-9==r7   	image_urlc                     t        |      }|s| S t        | |      }t        ||      }|s%t        j	                  d|j
                   |        yt        j                  |      j                  d      }d| S )z
    Convert an image URL to base64 encoded string.
    
    Args:
        image_url: URL or path to the image.
        db: Database session.
        
    Returns:
        Base64 encoded image string with data URI prefix, or None if conversion fails.
    r   zNo content found at: Nzutf-8zdata:image/png;base64,)	r   r   r   r   r   r{   base64	b64encodedecode)r   r   rT   r   rU   b64_encodeds         r    get_b64_imager     s~      2&J"9j9J3J
K .z/E/E.FzlST""#89@@IK#K=11r7   image_contentc                 v    	 t        |       S # t        $ r"}t        j                  d|        Y d}~yd}~ww xY w)a3  
    Extract image resolution (width, height) from image content.
    
    Supports PNG and JPEG image formats by parsing their binary headers.
    
    Args:
        image_content: Raw bytes of the image file.
        
    Returns:
        Tuple of (width, height) in pixels, or None if parsing fails.
    z&Error while parsing image resolution: N)_parse_image_resolutionr   r   r   )r   r   s     r    get_image_resolutionr   5  s9    &}55 =aSABs   
 	838c                 n    t        |       dk  ry| dd dk(  rt        |       S | dd dk(  rt        |       S y)z
    Parse image resolution from binary content for supported formats.
    
    Args:
        image_content: Raw bytes of the image file.
        
    Returns:
        Tuple of (width, height) in pixels, or None if format not supported.
       N   s   PNG

r\   s   )rS   _parse_png_resolution_parse_jpeg_resolution)r   s    r    r   r   H  sP     =B Ra00$]33 
r	k	)%m44r7   c                 `    t        |       dk  ryt        j                  d| dd       \  }}||fS )z
    Parse PNG image resolution from IHDR chunk.
    
    Args:
        image_content: PNG image bytes.
        
    Returns:
        Tuple of (width, height) in pixels.
    r   Nz>II   )rS   structunpack)r   widthheights      r    r   r   `  s;     =B MM%r")=>ME6&=r7   c                    d}|t        |       dz
  k  rq| |   dk7  r	 y
| |dz      }|dv rt        | |      S |dz   t        |       k\  r	 y
t        j                  d| |dz   |dz          d	   }|d|z   z  }|t        |       dz
  k  rqy
)z
    Parse JPEG image resolution from SOF (Start of Frame) marker.
    
    Args:
        image_content: JPEG image bytes.
        
    Returns:
        Tuple of (width, height) in pixels, or None if SOF not found.
    r\   	      r/   )               z>H   r   N)rS   _extract_jpeg_dimensionsr   r   )r   indexmarkerlengths       r    r   r   r  s     E
#m$q(
(4'  uqy) --+M5AA 19M**
  t]519UQY%GHKV! #m$q(
($ r7   	sof_indexc                     	 t        j                  d| |dz   |dz          \  }}}||fS # t         j                  t        f$ r Y yw xY w)z
    Extract dimensions from JPEG SOF marker.
    
    Args:
        image_content: JPEG image bytes.
        sof_index: Index of the SOF marker.
        
    Returns:
        Tuple of (width, height) in pixels.
    z>HHHr   r   N)r   r   r   
IndexError)r   r   r`   r   r   s        r    r   r     sW    !==M)a-	A>
65 f}LL*% s   &) AAc           	      >   d}d}| D ]{  }t        |d      rt        |d      st        j                  d       1|j                  }|s$t        j	                  dt        |dd              ct        ||      }||d   z  }|dz  }} ||d	z  |d
z  |d}t        |       |S )a^  
    Calculate and log sizes of email attachments.
    
    Processes a collection of mail attachments and calculates their individual
    and total sizes in bytes, KB, and MB. Logs detailed size information.
    
    Args:
        mail_attachments: Collection of attachment objects with 'byte_content' 
                         and 'original_name' attributes.
                         
    Returns:
        Dictionary containing total size statistics with keys:
        - 'total_bytes': Total size in bytes
        - 'total_kb': Total size in kilobytes  
        - 'total_mb': Total size in megabytes
    r   byte_contentr   zDAttachment missing required attributes (byte_content, original_name)zEmpty attachment: Unknownbytesr/   r,   r-   )total_bytestotal_kbtotal_mbattachment_count)rC   r   r   r   r   getattr%_calculate_individual_attachment_size_log_total_attachment_sizes)mail_attachmentstotal_size_bytesr   
attachmentr   	size_infototal_statss          r    calculate_attachment_sizesr    s    " & 
z>2'*o:^NNab!..KK,WZR[-\,]^_9*lS	Ig..A  ($t+$4,	K  ,r7   r   c                     t        |      }|dz  }|dz  }|||d}t        j                  d| j                   d| d|dd|dd	       |S )	a  
    Calculate size metrics for a single attachment.
    
    Args:
        attachment: Attachment object with original_name attribute.
        byte_content: Raw bytes of the attachment.
        
    Returns:
        Dictionary with size metrics in different units.
    r,   )r   kbr<   zAttachment: z	 - Size:  bytes (r=    KB,  MB))rS   r   r   r   )r   r   
size_bytessize_kbsize_mbr   s         r    r   r     sy     \"J4GnG I KK
z//0 1HWSMwsm4	I
 r7   r  c                 d    t         j                  d| d    d| d    d| d   dd| d	   dd
	       y)z
    Log total attachment size statistics.
    
    Args:
        total_stats: Dictionary containing total size statistics.
    zTotal Attachments (r   z files) - Size: r   r  r   r=   r  r   r  N)r   r   )r  s    r    r   r     sV     KK
k*<=> ?]+, -
#C(k*.Ec-J$	Pr7   )r   N)N)r   N)J__doc__r   r   loggingr   r   r   pathlibr   typingr   r   r   r   r   botocore.exceptionsr	   fastapir
   r   sqlalchemy.ormr   src.apps.base.servicesr   src.core.configr   src.core.exceptionsr   src.core.utils.constantsr   src.core.utils.enumsr   src.apps.base.utils.functionsr   	getLoggerru   r   r!   strr(   rc   floatr6   r1   rI   rL   rV   tuplerm   r   rw   r~   r   boolr   r   r   r   r   r   r   rQ   rR   r   r   r   r   r   r   r   r   r  r   r   rK   r7   r    <module>r     s    	  	    . .  + $ " 1 $ , 7 . <			8	$$  *"C "} " "* @M?O?O C } TW ,1C 1C 1(s  42 2sC}9M 22
% 
D 
(#c #S #U3PS8_ #0 Cy	00
0 0 	0
 d^0fS% Ss SQT SY] S2 #3i
  
$Z	>;S ;T ; 
6C 
6C 
6   	''' ' SM	'
 
'TU # $ : !
SM e_> % 0# c (>C >HUO >&2S 2g 2(3- 24 (5c?2K &5 XeCHo5N 0 8E#s(O3L $% HU38_4M BE c huUXZ]U]F_ *+De4D +\E dSVX]S]N^ :T#u*-= $ r7   