
    Ei+                     H   d dl mZmZmZ d dlmZ d dlmZ d dlm	Z	 d dl
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mZmZmZ  ee      Z e       Zej8                  Zej:                  Zej<                  Zej<                  Zej@                  Z!defdZ"defdZ#defdZ$defdZ%defdZ&dee	   ddfdZ'e!fdede	de(de	fdZ)dej
                  de(de(dej
                  fdZ*defdZ+	 	 d)de	de	d ee(e(f   d!e,de	f
d"Z-de	de	d#e(d$e(de	f
d%Z.d&e	d'e	de	fd(Z/y)*    )
UploadFileHTTPExceptionstatus)Image)BytesIO)PathN)OptionalTuple)PSDImage)setup_logger)get_settings)ERR_IMG_WIDTH_HEIGHTERR_FILE_TOO_LARGEERR_PSD_EXTERR_OVERLAY_EXTfilec                    | j                   j                         }t        j                  t	        |            }|j
                  j                         }ddg}||vrt        ddj                  |       d      t        |      t        kD  rt        dt        dz  dz  dd	      | j                   j                  d
       y )NjpegpngInvalid image format. Only ,  are allowed.(File is too large. Maximum file size is    .2fMB.r   )r   readr   openr   formatlower
ValueErrorjoinlenMAX_PROFILE_IMG_SIZEseekr   
image_dataimageimage_formatallowed_formatss        A/var/www/html/story-book/Story-Book-python-api/app/utils/image.pyvalidate_profile_imager,      s    !JJJwz*+E<<%%'LuoO?*6tyy7Q6RR_`aa
:--CDX[_D_bfDfgjCkknoppIINN1    c                    | j                   j                         }t        j                  t	        |            }|j
                  j                         }g d}||vrt        ddj                  |       d      t        |      t        kD  rt        dt        dz  dz  dd      | j                   j                  d	       y )
N)psdr   jpgr   r   r   r   r   r   r   r   r   )r   r   r   r   r   r   r    r!   r"   r#   MAX_PRODUCT_IMG_SIZEr%   r&   s        r+   validate_product_imagesr2   )   s    !JJJwz*+E<<%%'L3O?*6tyy7Q6RR_`aa
:--CDX[_D_bfDfgjCkknoppIINN1r-   c                     dg}| j                   j                  d      d   j                         }||vrt        t              | j
                  j                  d       y )Nr/   .r   )filenamesplitr    r!   r   r   r%   r   allowedexts      r+   validate_base_filer;   4   sN    gG
--

c
"2
&
,
,
.C
'%%IINN1r-   c                     g d}| j                   j                  d      d   j                         }||vrt        t              | j
                  j                  d       y )Nr   r0   r   r4   r5   r   )r6   r7   r    r!   r   r   r%   r8   s      r+   validate_overlay_filer>   ;   sL    $G
--

c
"2
&
,
,
.C
'))IINN1r-   c                     g d}| j                   j                  d      d   j                         }||vrt        d      | j                  j                  d       y )Nr=   r4   r5   z/Invalid logo/stamp image. Must be PNG/JPG only.r   )r6   r7   r    r!   r   r%   r8   s      r+   validate_logo_filer@   B   sM    $G
--

c
"2
&
,
,
.C
'JKKIINN1r-   pathreturnc                     | sy	 | j                         r| j                          yy# t        $ r t        j	                  d|        Y yw xY w)aV  
    Safely delete a file from disk.

    This helper removes files while silently ignoring missing paths
    and logging any unexpected exceptions.

    Args:
        path (Optional[Path]): Path to delete. No action if None.
    Notes:
        - Used for cleanup during partial failures.
        - Never raises exceptions to the caller.
    NzFailed to delete file: %s)existsunlink	Exceptionlogger	exception)rA   s    r+   safe_deleterI   I   sF     <;;=KKM  <4d;<s    ' A	A	upload	directory	max_bytesc                   K   t        |        t        | j                  xs d      j                  }t	        j
                         j                   d| }||z  }d}	 |j                  d      5 }	 | j                  t               d{   }|sn_|j                  |       |t        |      z  }||kD  r:|j                          t        |       t        t        j                   t"              	 ddd       	 | j                          d{    |S 7 # 1 sw Y   &xY w7 # t$        $ r Y |S w xY w# 	 | j                          d{  7   w # t$        $ r Y w w xY wxY ww)aq  
    Save an uploaded file to disk using streaming I/O.

    This prevents loading large files fully into RAM, making it safe
    for high-volume uploads.

    Args:
        upload (UploadFile): File uploaded by the client.
        directory (Path): Directory where the file should be saved.
        max_bytes (int): Maximum allowed upload size in bytes.
    Returns:
        Path: Full path to the saved file.
    Raises:
        HTTPException: 413 if the file exceeds the maximum allowed size.
    Notes:
        - Uses CHUNK_SIZE to read file incrementally.
        - Filename is sanitized and made unique using UUID.
    rJ   _r   wbN)status_codedetail)r2   r   r6   nameuuiduuid4hexr   r   
CHUNK_SIZEwriter#   closerI   r   r   !HTTP_413_REQUEST_ENTITY_TOO_LARGEr   rF   )	rJ   rK   rL   r6   unique_filename	file_pathbytes_writtenbufferchunks	            r+   save_upload_file_streamedr_   ^   sE    . F#FOO/x055H))*!H:6OO+IM^^D!V$kk*55U#U+ 9,LLN	*'F4\4\ewxx  	 "	,,.   # 6 "! ! 			,,.   		s   AE D2 -DDA&D/D2 8D" D D" E DDD2  D" "	D/+E .D//E 2E4EE
EE	EEEEE imgtarget_wtarget_hc                 \   | j                   \  }}|dk(  s|dk(  rt        t              t        ||z  ||z        }t	        ||z        t	        ||z        }}| j                  ||ft        j                  j                        }||z
  dz  }	||z
  dz  }
|j                  |	|
|	|z   |
|z   f      S )a-  
    Resize an image while maintaining aspect ratio, then center-crop it.

    Ensures the output completely fills the target size
    without letterboxing.

    Args:
        img (Image.Image): Input PIL image.
        target_w (int): Output width.
        target_h (int): Output height.
    Returns:
        Image.Image: Resized + cropped PIL image.
    Raises:
        ValueError: If image has zero width/height.
    Notes:
        - Uses LANCZOS for high-quality resampling.
        - Commonly used for fitting overlay images into PSD placeholders.
    r      )
sizer!   r   maxintresizer   
ResamplingLANCZOScrop)r`   ra   rb   owohscalenew_wnew_himg_resizedleftuppers              r+   resize_and_crop_to_fillrt      s    & XXFB	Qw"'-..2x"}-ErEz?CU
O5E**eU^U-=-=-E-EFKH"DX!#ET5$/58;KLMMr-   r/   c           	      d   	 | j                         D cg c]  }|t        |dd      f }}|D ]  \  }}	 d|_         	 d|_        | j	                         j                  d      }|j                         d   }|D ]  \  }}	 ||_         |j                  |k7  r*|j                  |t        j                  j                        }|S c c}w # t        $ r Y w xY w# t        $ r Y w xY w# t        $ r Y yw xY w# t        $ r/ t        j                  d       t        j                  d|d      cY S w xY w)	a\  
    Render the full-size alpha mask of a given PSD layer.

    This isolates a single layer by temporarily hiding all others,
    composites the PSD, extracts the alpha channel, and restores visibility.

    Args:
        psd (PSDImage): Loaded PSD object.
        target_layer: PSD layer whose alpha mask is required.
        canvas_size (tuple): Target (width, height) output resolution.
    Returns:
        Image.Image: Grayscale ("L") alpha mask for the layer.
    Notes:
        - Always returns an image even if rendering fails.
        - Non-blocking, used heavily during overlay placement.
    visibleTFRGBAr5   z"Failed to compute layer alpha maskL   )descendantsgetattrrv   rF   	compositeconvertr7   re   rh   r   ri   rj   rG   rH   new)	r/   target_layercanvas_sizelayer	vis_staterN   renderedalphaoriginal_viss	            r+   render_layer_alpha_fullsizer      sN   "&0 *
* GE9d34* 	 
 "HE1 % "	#'L  ==?**62 $ $-E< , $- ::$LLe.>.>.F.FGEA
    		    0=>yyk3//0s   C7 C
C7 C
C7 C :C7 C(<C7 C7 
	CC7 CC7 	C%"C7 $C%%C7 (	C41C7 3C44C7 75D/.D/src_pathdest_dir
thumb_sizepreserve_aspectc                 (   	 t        |      }|j                  dd       t        j                  |       5 }|j	                  d      }|r-|j                  |t        j                  j                         |}n*|j                  |t        j                  j                        }t        j                         j                   d}||z  }|j                  |d       |cddd       S # 1 sw Y   yxY w# t        $ r t        j                  d|         w xY w)	a7  
    Create a thumbnail for the given image using ONLY RESIZE (no cropping).
    - preserve_aspect=True: image is proportionally resized to fit inside the box.
    - preserve_aspect=False: image is resized exactly to the provided dimensions (may distort).

    Returns:
        Path to the saved thumbnail.
    Tparentsexist_okrw   z
_thumb.pngPNGr   Nz!Failed to create thumbnail for %s)r   mkdirr   r   r}   	thumbnailri   rj   rh   rS   rT   rU   saverF   rG   rH   )r   r   r   r   r`   thumbrR   	dest_paths           r+   create_thumbnail_from_pathr      s    >td3ZZ!S++f%Cj%*:*:*B*BC 

:u/?/?/G/GH jjl&&'z2D 4IJJyJ/! "!!$  <hGs)   3C0 B%C$	C0 $C-)C0 -C0 0!Dtarget_widthtarget_heightc                 N   	 t        |      }|j                  dd       t        j                  |       5 }|j	                  d      }|j
                  \  }}t        ||z  ||z  d      }t        ||z        }t        ||z        }	|j                  ||	ft        j                  j                        }
t        j                         j                   d}||z  }|
j                  |d       |cddd       S # 1 sw Y   yxY w# t        $ r t         j#                  d	| ||        w xY w)
aJ  
    Resize an image to fit INSIDE (target_width, target_height) while:
    - maintaining aspect ratio,
    - never cropping,
    - never distorting,
    - avoiding upscaling (keeps original if already smaller).

    This produces a product-sized image that is as large as possible
    without exceeding the given dimensions.
    Tr   rw   g      ?z_product.pngr   r   Nz-Failed to resize image %s to fit inside %sx%s)r   r   r   r   r}   re   minrg   rh   ri   rj   rS   rT   rU   r   rF   rG   rH   )r   r   r   r   r`   orig_worig_hratioro   rp   resizedrR   r   s                r+   resize_image_to_sizer     s    >td3ZZ!S++f%C !XXNFF v-}v/EsKE'E'Ejj%1A1A1I1IJGjjl&&'|4D 4ILL5L1% "!!(  ;lM	
 	s)   3D B6C5+	D 5C>:D >D #D$
final_pathcolor_layer_pathc                    t        j                  |       j                  d      5 }t        j                  |      j                  d      5 }|j                  |j                  k7  r4|j	                  |j                  t         j
                  j                        }t        j                  ||      }|j                  | dd       ddd       ddd       | S # 1 sw Y   xY w# 1 sw Y   | S xY w)z
    Composite color_layer (RGBA, transparent where not colored) on top of final_path.
    Overwrites final_path with the composite and returns final_path (Path).
    rw   r   b   )r   qualityN)	r   r   r}   re   rh   ri   rj   alpha_compositer   )r   r   	final_img	color_imgr|   s        r+   apply_color_layer_to_imager   D  s    
 
J		'	'	/9	$	%	-	-f	5>>Y^^+!((9I9I9Q9QRI)))Y?	z%< 
6 
0  
6	5 
0 s$   %C 
A8CC C	C  C*))  r   T)0fastapir   r   r   PILr   ior   pathlibr   rS   typingr	   r
   	psd_toolsr   app.core.loggingr   app.core.configr   app.core.constantsr   r   r   r   __name__rG   settingsrV   r$   r1   MAX_PSD_SIZEMAX_PRODUCT_UPLOAD_BYTESMAX_UPLOAD_BYTESr,   r2   r;   r>   r@   rI   rg   r_   rt   r   boolr   r   r    r-   r+   <module>r      s   5 5     "   ) (  
h	>   
44 ,,44 44 	 		* 	Z 
 Z <htn < <0 &333 3 
	3jN N Ns Nu{{ NB70X 70x #- 	&&& c3h& 	&
 
&P--- - 	-
 
-^4 4 D r-   