from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
import random
from datetime import datetime,date,timedelta
from django.conf import settings
from passlib.hash import django_pbkdf2_sha256 as handler
import json
from rest_framework.authentication import get_authorization_header
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework_simplejwt.backends import TokenBackend
from rest_framework import exceptions
from django.shortcuts import render, redirect,HttpResponse
import math, random, string
from django.db.models import Max
from django.db.models import Q
from django.core.mail import send_mail
from datetime import datetime, timedelta
from .models import ProductSample,EndUser,Shoots,UserActivity,InReviewProductData,ReviewDataComments,Notifications,CredSettings,CollectionsProduct,AddCollectionProduct,Watchers
from .serializers import ProductSampleSerializer,EndUserSerializer,ProductSampleSerializerClient
import pandas as pd
from rest_framework.pagination import PageNumberPagination
from panel_api.functions import *
from .authentication import authenticated
from rest_framework.authentication import get_authorization_header
import re
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string
from django.utils.html import strip_tags
from datetime import datetime
from django.utils import timezone
import openai
from django.core.management.base import BaseCommand
from django.utils.timezone import now, timedelta
from django.utils.timezone import now, make_aware, is_naive
import dropbox
from .tasks import send_comment_notification_email
from django.db.models import F
from dropbox.files import CreateFolderError, SharedLink
from dropbox.exceptions import ApiError
from dropbox.sharing import CreateSharedLinkWithSettingsError
import paramiko
import os
import requests
from django.db.models.functions import ExtractMonth, ExtractYear
from collections import defaultdict
from django.db.models.functions import TruncMonth
from django.db.models import Count
from django.core.paginator import Paginator
from django.contrib.auth.hashers import make_password
from django.core.mail import send_mail
from django.core import mail



APP_KEY = settings.DROPBOX_APP_KEY
APP_SECRET = settings.DROPBOX_APP_SECRET
REFRESH_TOKEN = settings.DROPBOX_REFRESH_TOKEN


dbx = dropbox.Dropbox(
	oauth2_refresh_token=REFRESH_TOKEN,
	app_key=APP_KEY,
	app_secret=APP_SECRET
)


# NAS connection details
hostname = "100.71.115.36"
port = 22
username = "lionshead"
password = "Scr@tch1424"
remote_base_path = "/volume1/Projects_In_Progress_LH2"

# Create your views here.

def humanize_field_name(field_name):
	return field_name.replace('_', ' ').title()

def filter_images_by_article_number(files, tm_article_number):
	"""
	Filter image files that match the pattern: {tm_article_number}_XX.ext
	Example: If tm_article_number is 'L12101', matches 'L12101_01.png', 'L12101_02.png', etc.
	"""
	if not tm_article_number:
		return []
	
	image_extensions = ['.jpg', '.jpeg', '.png', '.webp']
	# Pattern: article_number followed by underscore, then digits, then extension
	pattern = re.compile(rf'^{re.escape(tm_article_number)}_\d+\.(jpg|jpeg|png|webp)$', re.IGNORECASE)
	
	filtered_files = []
	for f in files:
		if any(f.lower().endswith(ext) for ext in image_extensions):
			if pattern.match(f):
				filtered_files.append(f)
	
	return filtered_files



def get_dropbox_folder_contents(shared_url):
	try:
		dbx = dropbox.Dropbox(
			oauth2_refresh_token=REFRESH_TOKEN,
			app_key=APP_KEY,
			app_secret=APP_SECRET
		)

		shared_link = SharedLink(url=shared_url)
		result = dbx.files_list_folder(path="", shared_link=shared_link)

		files = []
		for entry in result.entries:
			if isinstance(entry, dropbox.files.FileMetadata):
				try:
					# Try to create a shared link
					link_metadata = dbx.sharing_create_shared_link_with_settings(entry.path_lower)
					file_url = link_metadata.url
				except ApiError as e:
					# Handle case where shared link already exists
					if isinstance(e.error, dropbox.sharing.CreateSharedLinkWithSettingsError) and e.error.is_shared_link_already_exists():
						# Fetch existing shared links
						existing_links = dbx.sharing_list_shared_links(path=entry.path_lower, direct_only=True)
						if existing_links.links:
							file_url = existing_links.links[0].url
						else:
							file_url = None
					else:
						raise e
				print(file_url,' -------------file_urlfile_url')
				if file_url:
					file_url = file_url.replace("?dl=0", "?raw=1")  # or ?preview=1
					files.append({
						"name": entry.name,
						"url": file_url
					})

		return files

	except Exception as e:
		return {"error": str(e)}



class UploadImages(APIView):
	def post(self, request):
		try:
			data = request.data
			images = data.getlist('images')
			image_urls = []
			for image in images:
				image_path = uploadTheProfile(image)
				image_urls.append(image_path)
			return Response({'status_code':status.HTTP_200_OK,'status_message':'Success','data':image_urls})
		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

def generate_job_number():
	current_year = datetime.now().year
	prefix = "SDR"

	# Fetch all job_numbers for the current year
	existing = ProductSample.objects.filter(job_number__endswith=f"-{current_year}")

	# Extract sequence numbers
	sequences = []
	for obj in existing:
		match = re.match(rf"{prefix}-(\d+)-{current_year}", obj.job_number)
		if match:
			sequences.append(int(match.group(1)))

	next_seq = max(sequences) + 1 if sequences else 1
	sequence_str = str(next_seq).zfill(2)

	return f"{prefix}-{sequence_str}-{current_year}"

class UserRegister(APIView):
	def post(self,request):
		try:
			data = request.data
			name = data.get('name')
			email = data.get('email').strip().replace(" ", "").lower()
			password = data.get('password')
			if not email:
				return Response({"message":'email is required'},status=status.HTTP_400_BAD_REQUEST)
			if not name:
				return Response({"message":'name is required'},status=status.HTTP_400_BAD_REQUEST)
			if not password:
				return Response({"message":'password is required'},status=status.HTTP_400_BAD_REQUEST)

			new_password = handler.hash(password)
			check_email = EndUser.objects.filter(email=email).first()
			if check_email:
				return Response({"message":"The email is already registered"},status=status.HTTP_409_CONFLICT)
			else:
				user_obj = EndUser.objects.create(name=name,email=email,password=new_password)
			return Response({"message":'You have been successfully registered with us.'})
		except Exception as e:
			return Response({"message":str(e)},status=status.HTTP_500_INTERNAL_SERVER_ERROR) 
	
	 
class LoginUser(APIView):
	def post(self,request):
		try:
			data = request.data
			email = data.get('email').strip().lower()
			password = data.get('password')
			if not email:
				return Response({"message":'email is required'},status=status.HTTP_400_BAD_REQUEST) 
			if not password:
				return Response({"message":'password is required'},status=status.HTTP_400_BAD_REQUEST)  
			user  = EndUser.objects.filter(email=email,is_client = False).count()
			if user == 0:
				return Response({"message":'This email does not exist in our database, please register'},status=status.HTTP_404_NOT_FOUND)
			
			userObj  = EndUser.objects.filter(email=email).first()

			userObj_delete  = EndUser.objects.filter(email=email,end_date__isnull = False).first()
			check_password = userObj.password
			check = handler.verify(password,check_password)
		
			if check:
				refresh_token = RefreshToken.for_user(userObj)
				allData={'id':userObj.id,
					'name':userObj.name,
					'email':userObj.email,
					'is_profile_completed':userObj.is_profile_completed,
					 'is_password_reset':userObj.is_password_reset,

				   }
				tokens={
					'refresh': str(refresh_token),
					'access': str(refresh_token.access_token),
				}
				return Response({"message":'Login Successfully','data':allData,'tokens':tokens})
			else:
				return Response({"message":'Invalid password'},status=status.HTTP_401_UNAUTHORIZED)
		except Exception as e:
			return Response({"message":str(e)},status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class ResendOTP(APIView):
	def post(self, request):
		try:
			data = request.data
			email = data.get("email", "").strip().lower()

			if not email:
				return Response({"message": "Email is required"}, status=status.HTTP_400_BAD_REQUEST)
			user_obj = EndUser.objects.filter(email=email).first()
			if not user_obj:
				return Response(
					{"message": "This email does not exist in our database."},
					status=status.HTTP_404_NOT_FOUND
				)
			new_otp = random.randrange(1000, 9999, 5)
			user_obj.forgot_password_otp = new_otp
			user_obj.save()
			subject = "Resend OTP - Forget Password"
			html_message = render_to_string("forget_password_otp.html", {"otp": new_otp})
			from_email = settings.EMAIL_HOST_USER

			mail.send_mail(
				subject,
				html_message,
				from_email,
				[email],
				html_message=html_message
			)

			return Response({
				"message": f"OTP resent successfully to {email}",
				"email": email
			}, status=status.HTTP_200_OK)

		except Exception as e:
			return Response({"message": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class userForgetPassword(APIView):
	def post(self,request):
		try:
			data = request.data
			email =data.get('email').strip().lower()
			
			if not email:
				return Response({"message":'Email is required'})
			user =EndUser.objects.filter(email=email).count()
			if user == 0:
				return Response({"message":'This email does not exist in our database, please enter the valid email address.'},status=status.HTTP_404_NOT_FOUND)
			user_obj = EndUser.objects.filter(email= email).first()
			if user_obj:
				theotp=random.randrange(1000, 9999, 5)
				user_obj.forgot_password_otp=theotp
				user_obj.save()
				to_email = user_obj.email
				subject = "Forget Password OTP"
				html_message = render_to_string('forget_password_otp.html', {'otp': theotp})
				plain_message = html_message
				from_email = settings.EMAIL_HOST_USER
				mail.send_mail(subject, plain_message, from_email, [to_email], html_message=html_message)
				
				return Response({
					"message": f"OTP Send Successfully to {email}",
					"email": email
				})

					# return Response({"message":'Reset Password OTP has been sent to this email address'})
			else:
				return Response({"message":'Email not exists'})

		except Exception as e:
				print(e)
				return Response({"message":str(e)},status=status.HTTP_500_INTERNAL_SERVER_ERROR)

class verifyForgetPasswordOTP(APIView):
	def post(self,request):
		try:
			email = request.data.get('email').strip().lower()
			if not email:
				return Response({'status_code':status.HTTP_400_BAD_REQUEST,'status_message':'Email is required'},status=status.HTTP_400_BAD_REQUEST)
			otp = request.data.get('otp')
			if not otp:
						return Response({'status_code':status.HTTP_400_BAD_REQUEST,'status_message':'otp is Required'},status =status.HTTP_400_BAD_REQUEST)
			check_email = EndUser.objects.filter(email = email).exists()
			if not check_email:
				return Response({'status_code':status.HTTP_400_BAD_REQUEST,'status_message':'Email not exist'},status=status.HTTP_400_BAD_REQUEST)
			user_obj = EndUser.objects.filter(email= email,forgot_password_otp = otp).first()
			if user_obj:
					return Response({'status_code':status.HTTP_200_OK,'status_message':'Successfully Verified'})
			else:
				return Response({'status_code':status.HTTP_400_BAD_REQUEST,'status_message':'Invalid otp'},status=status.HTTP_400_BAD_REQUEST)
		except Exception as e:
				return Response({'status_code':status.HTTP_500_INTERNAL_SERVER_ERROR,'status_message':str(e)},status=status.HTTP_500_INTERNAL_SERVER_ERROR)    
			


class changeForgetPassword(APIView):
	def post(self,request):
		try:
			email = request.data.get('email').strip().lower()
			if not email:
				return Response({'message': 'email is required'}, status=status.HTTP_404_NOT_FOUND)
			user_obj = EndUser.objects.filter(email=email).first()
			if not user_obj:
				return Response({'message': 'user not found'}, status=status.HTTP_404_NOT_FOUND)
			new_password = request.data.get('new_password')
			if not new_password:
				return Response({'message':'new password is required'},status=status.HTTP_400_BAD_REQUEST)
			confirm_password = request.data.get('confirm_password')
			if not confirm_password:
				return Response({'message':'confirm password is required'},status=status.HTTP_400_BAD_REQUEST)
			if new_password != confirm_password:
				return Response({'message':'Password and confirm password are not same.'},status=status.HTTP_403_FORBIDDEN)
			encrypt_password = handler.hash(confirm_password)
			user_obj.password = encrypt_password
			user_obj.save()
			return Response({'message':'Changed Password Successfully'}) 
		except Exception as e:
			return Response({'message':str(e)},status=status.HTTP_500_INTERNAL_SERVER_ERROR)

class LoginThroughProxy(APIView):
	def post(self, request):
		try:
			# Extract email and password from the request
			data = request.data
			email = data.get('email')
			password = data.get('password')
			
			if not email or not password:
				return Response({"message": "Email and Password are required."}, status=status.HTTP_400_BAD_REQUEST)
			
			# Prepare the data to send to the LoginUser API
			login_data = {
				'email': email,
				'password': password,
			}

			# Call the LoginUser API
			login_url = 'http://95.111.228.198:9006/api-panel/user-login'  # Replace with the actual URL of your LoginUser API
			response = requests.post(login_url, json=login_data)

			# Check if the LoginUser API request was successful
			if response.status_code == 200:
				return Response(response.json(), status=status.HTTP_200_OK)
			else:
				return Response(response.json(), status=response.status_code)

		except Exception as e:
			return Response({"message": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

class ResetPassword(APIView):
	def post(self, request):
		try:
			data = request.data

			email = data.get("email", "").strip().lower()
			old_password = data.get("old_password")
			new_password = data.get("new_password")
			confirm_password = data.get("confirm_password")

			if not email:
				return Response({"message": "Email is required"}, status=status.HTTP_400_BAD_REQUEST)

			user_obj = EndUser.objects.filter(email=email).first()
			if not user_obj:
				return Response({"message": "User not found"}, status=status.HTTP_404_NOT_FOUND)

			if not old_password:
				return Response({"message": "Old password is required"}, status=status.HTTP_400_BAD_REQUEST)

			if not handler.verify(old_password, user_obj.password):
				return Response({"message": "Old password is incorrect"}, status=status.HTTP_400_BAD_REQUEST)

			if not new_password:
				return Response({"message": "New password is required"}, status=status.HTTP_400_BAD_REQUEST)

			if not confirm_password:
				return Response({"message": "Confirm password is required"}, status=status.HTTP_400_BAD_REQUEST)

			if new_password != confirm_password:
				return Response({"message": "New and confirm passwords do not match"}, status=status.HTTP_400_BAD_REQUEST)

			hashed_password = handler.hash(new_password)
			user_obj.password = hashed_password
			user_obj.save()

			return Response({"message": "Password updated successfully"}, status=status.HTTP_200_OK)

		except Exception as e:
			return Response({"message": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

		
	
class UpdateUserProfile(APIView):
	def get(self, request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message':str(e)},status=status.HTTP_401_UNAUTHORIZED)
			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'user not found'}, status=status.HTTP_404_NOT_FOUND)
			all_data = {
				'id':user_obj.id,
				'name':user_obj.name,
				'email':user_obj.email,
				'phone_number':user_obj.phone_number,
				'image':user_obj.image,

				'is_password_reset':user_obj.is_password_reset,
				'is_client':user_obj.is_client,
				'is_admin':user_obj.is_admin,
	

			}
			return Response({'status_code':status.HTTP_200_OK,'status_message':'Fetched Successfully','data':all_data})
		except Exception as e:
			return Response({'message':str(e)},status=status.HTTP_500_INTERNAL_SERVER_ERROR)
		

	def post(self,request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message':str(e)},status=status.HTTP_401_UNAUTHORIZED)
			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'user not found'}, status=status.HTTP_404_NOT_FOUND)
			name = request.data.get('name')
			if not name:
				return Response({'status_code':status.HTTP_400_BAD_REQUEST,'status_message':'name is required'})
			email = request.data.get('email')
			if not email:
				return Response({'status_code':status.HTTP_400_BAD_REQUEST,'status_message':'email is required'})
			
			check_email = EndUser.objects.filter(email= email).exclude(id=user_obj.id)
			if check_email:
				return Response({'status_code':status.HTTP_400_BAD_REQUEST,'status_message':'This email is already associated with some account.'})
			
			phone_number = request.data.get('phone_number')
			if not phone_number:
				return Response({'status_code':status.HTTP_400_BAD_REQUEST,'status_message':'phone_number is required'})
			
			
			check_number = EndUser.objects.filter(phone_number = phone_number).exclude(id=user_obj.id)
			if check_number:
				return Response({'status_code':status.HTTP_400_BAD_REQUEST,'status_message':'This phone number is already associated with some account.'})
			
			image = request.data.get('image')

			user_obj.name = name
			user_obj.email = email
			user_obj.phone_number = phone_number
			user_obj.image = image
			user_obj.save()
			all_data = {
				'id':user_obj.id,
				'name':user_obj.name,
				'email':user_obj.email,
				'phone_number':user_obj.phone_number,
				'image':user_obj.image,
				'is_client':user_obj.is_client,
				 'is_password_reset':user_obj.is_password_reset,
				 'is_admin':user_obj.is_admin,
	 
	   


			}

			return Response({'status_code':status.HTTP_200_OK,'status_message':'Profile updated successfully','data':all_data})
		except Exception as e:
			return Response({'message':str(e)},status=status.HTTP_500_INTERNAL_SERVER_ERROR)





class CustomProductPagination(PageNumberPagination):
	page_size = 10  # Default page size
	page_size_query_param = 'page_size'  # Query parameter to change page size
	max_page_size = 100  # Maximum page size

	def get_page_size(self, request):
		# Get the page size from query parameters, default to the defined page_size if not provided
		page_size = request.query_params.get(self.page_size_query_param, self.page_size)
		
		try:
			page_size = int(page_size)
		except ValueError:
			page_size = self.page_size  # Default to the default if invalid
		
		# Ensure the page_size does not exceed max_page_size
		if page_size > self.max_page_size:
			page_size = self.max_page_size
		
		return page_size

	def get_paginated_response(self, data):
		return Response({
			'status_code': 200,
			'status_message': 'Products fetched successfully',
			'total_items': self.page.paginator.count,
			'total_pages': self.page.paginator.num_pages,
			'current_page': self.page.number,
			'next': self.get_next_link(),
			'previous': self.get_previous_link(),
			'results': data
		})

class GetProductListing(APIView):
	def get(self, request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message':str(e)},status=status.HTTP_401_UNAUTHORIZED)
			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'user not found'}, status=status.HTTP_404_NOT_FOUND)
			search = request.query_params.get('search', None)
			is_archive = request.query_params.get("is_archive")
			if is_archive:
				samples = ProductSample.objects.filter(is_archive = True).order_by('-id')
			else:
				samples = ProductSample.objects.filter(is_archive = False).order_by('-id')
			is_drop = request.query_params.get("is_drop")
			if is_drop:
				samples = ProductSample.objects.filter(is_dropped = True).order_by('-id')
			
			# Filter by collection name
			collection_name = request.query_params.get('collection_name', None)
			if collection_name:
				# Filter products that belong to a collection with the specified name
				samples = samples.filter(
					addcollectionproduct__collection__collection_name__icontains=collection_name
				).distinct()
	
			if search:
				samples = samples.filter(
					Q(product_line__icontains=search) |
					Q(subfamily__icontains=search) |
					Q(tm_article_number__icontains=search) |
					Q(style_name__icontains=search) |
					Q(og_style_name__icontains=search) |
					Q(color_name__icontains=search) |
					Q(season__icontains=search) |
					Q(job_number__icontains=search) |
					Q(location__icontains=search)
				)
			# Sorting functionality
			sort_by = request.query_params.get('sort_by', None)
			sort_order = request.query_params.get('sort_order', 'asc')  # Default to 'asc'

			# If 'sort_by' is provided, apply sorting
			if sort_by:
				if sort_order == 'asc':
					samples = samples.order_by(sort_by)
				else:  # 'desc'
					samples = samples.order_by(f'-{sort_by}')
			paginator = CustomProductPagination()
			result_page = paginator.paginate_queryset(samples, request)
			serializer = ProductSampleSerializer(result_page, many=True)
   
			response_data = {
				'sort_by': sort_by,
				'sort_order': sort_order,
				'collection_name': collection_name,
				'results': serializer.data,
			}

			return paginator.get_paginated_response(response_data)

		except Exception as e:
			return Response({'message':str(e)},status=status.HTTP_500_INTERNAL_SERVER_ERROR)


def convert_date(date_str):
	try:
		# Define the current format (MM/DD/YY) and desired format (YYYY-MM-DD)
		current_format = "%m/%d/%y"
		desired_format = "%Y-%m-%d"
		
		# Convert the date string to the desired format
		date_obj = datetime.strptime(date_str, current_format)
		return date_obj.strftime(desired_format)
	except ValueError:
		# If the date format is invalid, return None or handle the error as needed
		return None


class UploadProductSample(APIView):
	def post(self, request, format=None):
		file = request.FILES.get('file')

		if not file:
			return Response({'error': 'No file uploaded'}, status=status.HTTP_400_BAD_REQUEST)
		
		# Get collection_id from request data (optional)
		collection_id = request.data.get('collection_id')
		collection_obj = None
		if collection_id:
			collection_obj = CollectionsProduct.objects.filter(id=collection_id).first()
			if not collection_obj:
				return Response({'error': f'Collection with id {collection_id} not found'}, status=status.HTTP_400_BAD_REQUEST)

		try:
			if file.name.endswith('.csv'):
				df = pd.read_csv(file)
			elif file.name.endswith('.xlsx'):
				df = pd.read_excel(file)
			else:
				return Response({'error': 'Unsupported file format'}, status=status.HTTP_400_BAD_REQUEST)
		except Exception as e:
			return Response({'error': f'Error reading file: {str(e)}'}, status=status.HTTP_400_BAD_REQUEST)

		# Process each row in the CSV/Excel file
		for _, row in df.iterrows():
			tm_article_number = row.get('TM ARTICLE NUMBER')
			if not tm_article_number:
				continue

			# Convert the PAR Date before using it in the defaults
			par_date = row.get('PAR Date')
			if par_date:
				par_date = convert_date(par_date)  # Convert the date format
			else:
				par_date = None  # Handle missing PAR Date case

			job_number = generate_job_number()

			defaults = {
				'product_line': row.get('PRODUCT LINE'),
				'subfamily': row.get('SUBFAMILY'),
				'style_name': row.get('STYLE NAME'),
				'og_style_name': row.get('OG STYLE NAME'),
				'color_name': row.get('COLOR  NAME'),
				'season': row.get('SEASON'),
				'par_date': par_date,
 
				'number_of_samples_received': row.get('Number of Samples Received') or None,
				'marketing_sample_estimated_date': row.get('Marketing Sample Estimated Date'),
				'rep_sms_photoshop_comments': row.get('Rep SMS/Photoshop Comments'),
				'location': row.get('LOCATION'),
				'sample_pass_off_date': row.get('Sample Pass off Date'),
				'lionshead_received_date': row.get('Lionshead Received Date'),
				'flatlay_shoot_date': row.get('Flatlay Shoot Date'),
				'flatlay_shoot': row.get('Flatlays Shot (Y or N)'),
				'on_model_shoot_date': row.get('On Model Shoot Date'),
				'on_models_shot': row.get('On Models Shot (Y or N)'),
				'sample_return_date': row.get('Sample Return Date'),
				'last_updated_datetime': row.get('Last Updated DateTime'),
				'updated_by': row.get('Updated By'),
				'is_product_shot': False,
				'job_number': job_number,
			}

			product_sample, _ = ProductSample.objects.update_or_create(
				tm_article_number=tm_article_number,
				defaults=defaults
			)

			# === Add product to collection if collection_id is provided ===
			if collection_obj:
				try:
					# Check if product is already in any collection
					existing_collection_product = AddCollectionProduct.objects.filter(product=product_sample).first()
					if existing_collection_product:
						existing_collection = existing_collection_product.collection
						# Skip if already in the same collection, log warning if in different collection
						if existing_collection.id != collection_obj.id:
							print(f"Warning: Product {tm_article_number} is already in collection {existing_collection.collection_name}, skipping collection assignment")
					else:
						# Product is not in any collection, add it to the specified collection
						AddCollectionProduct.objects.create(collection=collection_obj, product=product_sample)
				except Exception as e:
					# Log error but don't fail the upload for this product
					print(f"Error adding product {tm_article_number} to collection: {str(e)}")
			# === End collection assignment ===

		return Response({'message': 'File uploaded and data processed successfully.'}, status=status.HTTP_201_CREATED)




# class UploadProductSample(APIView):
#     def append_date(self, existing_list, new_date):
#         if not new_date:
#             return existing_list or []
#         if existing_list is None:
#             existing_list = []
#         if new_date not in existing_list:
#             existing_list.append(new_date)
#         return existing_list

#     def post(self, request, format=None):
#         file = request.FILES.get('file')

#         if not file:
#             return Response({'error': 'No file uploaded'}, status=status.HTTP_400_BAD_REQUEST)

#         try:
#             if file.name.endswith('.csv'):
#                 df = pd.read_csv(file)
#             elif file.name.endswith('.xlsx'):
#                 df = pd.read_excel(file)
#             else:
#                 return Response({'error': 'Unsupported file format'}, status=status.HTTP_400_BAD_REQUEST)
#         except Exception as e:
#             return Response({'error': f'Error reading file: {str(e)}'}, status=status.HTTP_400_BAD_REQUEST)

#         # Setup SSH
#         ssh = paramiko.SSHClient()
#         ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

#         try:
#             # These must be set in your settings or environment
#             # hostname = settings.SSH_HOST
#             # port = settings.SSH_PORT
#             # username = settings.SSH_USERNAME
#             # password = settings.SSH_PASSWORD
#             # remote_base_path = settings.REMOTE_BASE_PATH

#             ssh.connect(hostname, port=port, username=username, password=password, look_for_keys=False)
#             sftp = ssh.open_sftp()

#             for _, row in df.iterrows():
#                 tm_article_number = row.get('TM ARTICLE NUMBER')
#                 if not tm_article_number:
#                     continue

#                 # Try fetching existing product sample
#                 product_sample = ProductSample.objects.filter(tm_article_number=tm_article_number).first()

#                 # Convert each date to standardized format
#                 par_date = convert_date(row.get('PAR Date'))
#                 marketing_date = convert_date(row.get('Marketing Sample Estimated Date'))
#                 sample_pass_off_date = convert_date(row.get('Sample Pass off Date'))
#                 lionshead_received_date = convert_date(row.get('Lionshead Received Date'))
#                 flatlay_shoot_date = convert_date(row.get('Flatlay Shoot Date'))
#                 on_model_shoot_date = convert_date(row.get('On Model Shoot Date'))
#                 sample_return_date = convert_date(row.get('Sample Return Date'))
#                 last_updated_date = convert_date(row.get('Last Updated DateTime'))

#                 job_number = generate_job_number()

#                 defaults = {
#                     'product_line': row.get('PRODUCT LINE'),
#                     'subfamily': row.get('SUBFAMILY'),
#                     'style_name': row.get('STYLE NAME'),
#                     'og_style_name': row.get('OG STYLE NAME'),
#                     'color_name': row.get('COLOR  NAME'),
#                     'season': row.get('SEASON'),
#                     'par_date': self.append_date(product_sample.par_date if product_sample else [], par_date),
#                     'marketing_sample_estimated_date': self.append_date(product_sample.marketing_sample_estimated_date if product_sample else [], marketing_date),
#                     'number_of_samples_received': row.get('Number of Samples Received') or None,
#                     'rep_sms_photoshop_comments': row.get('Rep SMS/Photoshop Comments'),
#                     'location': row.get('LOCATION'),
#                     'sample_pass_off_date': self.append_date(product_sample.sample_pass_off_date if product_sample else [], sample_pass_off_date),
#                     'lionshead_received_date': self.append_date(product_sample.lionshead_received_date if product_sample else [], lionshead_received_date),
#                     'flatlay_shoot_date': self.append_date(product_sample.flatlay_shoot_date if product_sample else [], flatlay_shoot_date),
#                     'flatlay_shoot': row.get('Flatlays Shot (Y or N)'),
#                     'on_model_shoot_date': self.append_date(product_sample.on_model_shoot_date if product_sample else [], on_model_shoot_date),
#                     'on_models_shot': row.get('On Models Shot (Y or N)'),
#                     'sample_return_date': self.append_date(product_sample.sample_return_date if product_sample else [], sample_return_date),
#                     'last_updated_datetime': self.append_date(product_sample.last_updated_datetime if product_sample else [], last_updated_date),
#                     'updated_by': row.get('Updated By'),
#                     'is_product_shot': False,
#                     'job_number': job_number,
#                 }

#                 product_sample, _ = ProductSample.objects.update_or_create(
#                     tm_article_number=tm_article_number,
#                     defaults=defaults
#                 )

#                 # === NAS Folder Creation ===
#                 today_str = datetime.now().strftime("%Y_%m_%d")
#                 folder_name = f"{today_str}_{tm_article_number}"
#                 base_path = f"{remote_base_path}/{folder_name}"

#                 try:
#                     for sub in ["Raw Data", "Trash Later", "Final Files", "Digital Proofs", "Client Originals"]:
#                         ssh.exec_command(f'mkdir -p "{base_path}/{sub}"')

#                     product_sample.nas_folder_name = folder_name
#                     product_sample.save()
#                 except Exception as e:
#                     return Response({'error': f'Failed to create folders for {tm_article_number}: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

#                 # === Dropbox Folder Creation ===
#                 try:
#                     dbx = dropbox.Dropbox(settings.DROPBOX_ACCESS_TOKEN)
#                     dbx._session.headers.update({'Dropbox-API-Select-User': settings.DROPBOX_USER_ID})

#                     base_dropbox_path = f"/{folder_name}"
#                     for sub in ["Raw Data", "Trash Later", "Final Files", "Digital Proofs", "Client Originals"]:
#                         dbx.files_create_folder_v2(f"{base_dropbox_path}/{sub}")

#                     shared_link_metadata = dbx.sharing_create_shared_link_with_settings(base_dropbox_path)
#                     product_sample.dropbox_url = shared_link_metadata.url
#                     product_sample.save()
#                 except dropbox.exceptions.ApiError as e:
#                     return Response({'error': f'Dropbox API error: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
#                 except Exception as e:
#                     return Response({'error': f'Failed to create Dropbox folder: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

#         except paramiko.AuthenticationException:
#             return Response({'error': 'SSH Authentication failed'}, status=status.HTTP_401_UNAUTHORIZED)
#         except paramiko.SSHException as e:
#             return Response({'error': f'SSH connection error: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
#         except Exception as e:
#             return Response({'error': f'Unexpected error: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
#         finally:
#             try:
#                 if 'sftp' in locals():
#                     sftp.close()
#                 if ssh.get_transport() and ssh.get_transport().is_active():
#                     ssh.close()
#             except:
#                 pass

#         return Response({'message': 'File uploaded and data processed successfully, NAS and Dropbox folders created.'}, status=status.HTTP_201_CREATED)




class GetProductDetail(APIView):
	def post(self, request):
		try:
			# === Authenticate User ===
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'user not found'}, status=status.HTTP_404_NOT_FOUND)

			article_number = request.data.get('article_number')
			if not article_number:
				return Response({'error': 'article_number is required'}, status=status.HTTP_400_BAD_REQUEST)

			# === Get Product ===
			try:
				product = ProductSample.objects.get(tm_article_number=article_number)
			except ProductSample.DoesNotExist:
				return Response({'error': 'Product not found'}, status=status.HTTP_404_NOT_FOUND)

			serializer = ProductSampleSerializer(product)

			# === Dropbox Files ===
			# print(product.dropbox_url,' ---------product.dropbox_url')
			dropbox_files = []
			if product.dropbox_url:
				dropbox_files = get_dropbox_folder_contents(product.dropbox_url)

			# === Get NAS Images and Save Locally ===
			nas_files = []
			# Get collection for the product
			collection_product = AddCollectionProduct.objects.filter(product=product).first()
			if collection_product and collection_product.collection:
				collection_name = collection_product.collection.collection_name
				print(collection_name,' ------------collection_name')
				ssh = paramiko.SSHClient()
				ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

				try:
					ssh.connect(hostname, port=port, username=username, password=password, look_for_keys=False)
					sftp = ssh.open_sftp()

					# Fetch from collection's Client Originals folder
					nas_folder_path = f"{remote_base_path}/{collection_name}/Client Originals"
					print(nas_folder_path,' -----------nas_folder_path')
					try:
						files = sftp.listdir(nas_folder_path)
					except FileNotFoundError:
						files = []
					print(files,' ----------files')
					# Filter images by article number pattern (e.g., L12101_01.png, L12101_02.png)
					image_files = filter_images_by_article_number(files, product.tm_article_number)
					print(image_files,' --------------image_files')
					# Local destination
					local_dir = os.path.join(settings.MEDIA_ROOT, 'nas_images', product.nas_folder_name or collection_name)
					os.makedirs(local_dir, exist_ok=True)
					print(local_dir,' ----------local_dir')
					for filename in image_files:
						remote_path = f"{nas_folder_path}/{filename}"
						local_path = os.path.join(local_dir, filename)

						# Skip if already exists
						if not os.path.exists(local_path):
							sftp.get(remote_path, local_path)

						# Return media URL
						media_url = f"{settings.MEDIA_URL}nas_images/{product.nas_folder_name or collection_name}/{filename}"
						nas_files.append(media_url)
					print(nas_files,' ------------nas_files')
				except Exception as e:
					return Response({'error': f'Failed to retrieve NAS files: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
				finally:
					try:
						sftp.close()
						ssh.close()
					except:
						pass
			# === Get Watchers ===
			watchers = Shoots.objects.filter(product=product, is_watcher=True)
			watcher_users = []
			for watcher in watchers:
				user = watcher.user
				if user:
					watcher_users.append({
						'id': user.id,
						'name': user.name,
						'email': user.email,
						'phone_number': user.phone_number,
						'image': user.image,
					})
	 
			main_watchers = Watchers.objects.filter(product=product)
			main_watcher_users = []
			for watcher in main_watchers:
				if user:
					main_watcher_users.append({
						'id': watcher.id,
						'email': watcher.email,
					})
			#========Job history===============
   
			history = UserActivity.objects.filter(job_number = article_number)
			history_data = []
			if history:
				for his in history:
					all_data = {
						'id':his.id,
						'message':his.message,
						'job_number':his.job_number,
						'start_date':his.start_date,
					}
					history_data.append(all_data)
			# === Final Response ===
			response_data = serializer.data
			response_data["dropbox_files"] = dropbox_files
			response_data["nas_files"] = nas_files
			response_data["watchers"] = watcher_users  # Add watcher users to the response
			response_data["history"] = history_data 
			response_data["main_watchers"] = main_watcher_users


			return Response(response_data, status=status.HTTP_200_OK)

		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)




class ChangeProductStatus(APIView):
	def post(self,request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message':str(e)},status=status.HTTP_401_UNAUTHORIZED)
			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'user not found'}, status=status.HTTP_404_NOT_FOUND)
			product_status = request.data.get('status')
			if not status:
				return Response({'message': 'status is required'}, status=status.HTTP_404_NOT_FOUND)
			job_number = request.data.get('job_number')
			if not job_number:
				return Response({'message': 'job number is required'}, status=status.HTTP_404_NOT_FOUND)

			product_obj = ProductSample.objects.filter(job_number = job_number).first()
			if not product_obj:
				return Response({'message': 'no product found'}, status=status.HTTP_404_NOT_FOUND)
			product_obj.status = product_status
			product_obj.save()

			if product_status == "Retouching Done":

				# Send email to all admin users
				admin_users = EndUser.objects.filter(is_admin=True)
				subject = "Retouching process completed – Lionshead Studios"
				context = {
					'job_number': job_number,
					'product_status': product_status,
					'url': f"https://lionshead.tidera.ai/admin/products/{product_obj.tm_article_number}"

				}
				html_content = render_to_string('retouch_complete_email.html', context)
				text_content = strip_tags(html_content)
			elif product_status == "Review Success":
				 # Send email to all admin users
				nas_files = []
	
				# Get collection for the product
				collection_product = AddCollectionProduct.objects.filter(product=product_obj).first()
				if collection_product and collection_product.collection:
					collection_name = collection_product.collection.collection_name
					ssh = paramiko.SSHClient()
					ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
					try:
						ssh.connect(hostname, port=port, username=username, password=password, look_for_keys=False)
						sftp = ssh.open_sftp()

						# Fetch from collection's Client Originals folder
						nas_folder_path = f"{remote_base_path}/{collection_name}/Client Originals"
						try:
							files = sftp.listdir(nas_folder_path)
							print("Files in Client Originals folder:", files) # Log files retrieved
						except FileNotFoundError:
							files = []
							print("Client Originals folder not found")

						# Filter images by article number pattern (e.g., L12101_01.png, L12101_02.png)
						image_files = filter_images_by_article_number(files, product_obj.tm_article_number)
						print("Filtered image files by article number:", image_files) # Log filtered files

						final_folder_path = f"/Projects_In_Progress_LH2/{product_obj.nas_folder_name}/Final Files"
						
						# Create the final folder if it doesn't exist
						try:
							sftp.mkdir(final_folder_path)
						except Exception as e:
							print(f"Error creating final folder: {e}")

						for filename in image_files:
							remote_path = f"{nas_folder_path}/{filename}"
							final_remote_path = f"{final_folder_path}/{filename}"

							# Copy the file to the final folder (without moving it)
							try:
								with sftp.open(remote_path, 'rb') as source_file:
									file_data = source_file.read()

								with sftp.open(final_remote_path, 'wb') as destination_file:
									destination_file.write(file_data)

								# Dropbox upload
								user_id = settings.DROPBOX_USER_ID
								dbx._session.headers.update({'Dropbox-API-Select-User': user_id})

								with sftp.open(final_remote_path, 'rb') as f:
									dropbox_path = f"/{product_obj.nas_folder_name}/Final Files/{filename}"
									dropbox_folder_path = f"/{product_obj.nas_folder_name}/Final Files"

									dbx.files_upload(f.read(), dropbox_path, mute=True)
									folder_url = dbx.sharing_create_shared_link_with_settings(dropbox_folder_path).url
									product_obj.dropbox_url = folder_url
									product_obj.save()

								media_url = f"{settings.MEDIA_URL}nas_images/{product_obj.nas_folder_name}/{filename}"
								nas_files.append(media_url)

							except Exception as e:
								print(f"Error processing file {filename}: {str(e)}")

					except Exception as e:
						return Response({'error': f'Failed to retrieve and move NAS files: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
					finally:
						try:
							sftp.close()
							ssh.close()
						except:
							pass
				product_id = product_obj.id
				# Get admin users with verified emails
				admin_users = EndUser.objects.filter(is_admin=True, email_verified=True)

				# Get watchers related to the product
				watchers = Watchers.objects.filter(product_id=product_id).prefetch_related('user')

				# Get shoot users related to the product
				shoots = Shoots.objects.filter(product_id=product_id).prefetch_related('user')
		
				print([f.name for f in Watchers._meta.get_fields()])
				print([f.name for f in Shoots._meta.get_fields()])
		 
				# Collect all relevant users' emails
				email_user_map = {}

				# Admin users
				for user in admin_users:
					email_user_map[user.email] = user.name

				# Watchers
				for watcher in watchers:
					user = watcher.user
					if user.email_verified:
						email_user_map[user.email] = user.name

				# Shoots
				for shoot in shoots:
					user = shoot.user
					if user.email_verified:
						email_user_map[user.email] = user.name


				print("---------------------",email_user_map)

				subject = "Success:Client Review Process Completed – Lionshead Studios"
				context = {
					'job_number': job_number,
					'product_status': product_status,
				}
				html_content = render_to_string('review_complete_email.html', context)
				text_content = strip_tags(html_content)
			else:

				product_id = product_obj.id
				# Get admin users with verified emails
				admin_users = EndUser.objects.filter(is_admin=True, email_verified=True)

				# Get watchers related to the product
				watchers = Watchers.objects.filter(product_id=product_id).prefetch_related('user')

				# Get shoot users related to the product
				shoots = Shoots.objects.filter(product_id=product_id).prefetch_related('user')

				# Collect all relevant users' emails
				email_user_map = {}

				# Admin users
				for user in admin_users:
					email_user_map[user.email] = user.name

				# Watchers
				for watcher in watchers:
					user = watcher.user
					if user.email_verified:
						email_user_map[user.email] = user.name

				# Shoots
				for shoot in shoots:
					user = shoot.user
					if user.email_verified:
						email_user_map[user.email] = user.name
	  

				subject = "Requested Changes :Client Changes Request – Lionshead Studios"
				context = {
					'job_number': job_number,
					'product_status': product_status,
					'url': f"https://lionshead.tidera.ai/admin/products/{product_obj.tm_article_number}"
				}
				html_content = render_to_string('request_changes_email.html', context)
				text_content = strip_tags(html_content)

	
			# Loop through all admin users and send the email
			for email, name in email_user_map.items():
				print("---------------------",email)
				email = EmailMultiAlternatives(
					subject=subject,
					body=text_content,
					from_email=settings.EMAIL_HOST_USER,
					to=[email]
				)
				email.attach_alternative(html_content, "text/html")
				email.send()
	
	
			return Response({'status_code':status.HTTP_200_OK,'status_message':'Status updated successfully'})
		except Exception as e:
			return Response({'message':str(e)},status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class GetShoots(APIView):
	def get(self, request):
		try:
			# Authenticate the user
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)

			search = request.query_params.get('search', None)


			# Filter samples assigned to the current user only
			samples = ProductSample.objects.filter(
				status__isnull=False
			).order_by('-id')

			# Apply search if provided
			if search:
				samples = samples.filter(
					Q(product_line__icontains=search) |
					Q(subfamily__icontains=search) |
					Q(tm_article_number__icontains=search) |
					Q(style_name__icontains=search) |
					Q(og_style_name__icontains=search) |
					Q(color_name__icontains=search) |
					Q(season__icontains=search) |
					Q(location__icontains=search)
				)

			# Filter by status
			scheduled_samples = samples.filter(status__iexact='scheduled').order_by('order_index')
			in_progress_samples = samples.filter(status__iexact='in_progress').order_by('order_index')
			retouching_samples = samples.filter(status__iexact='retouching').order_by('order_index')
			revisions_samples = samples.filter(status__iexact='revisions').order_by('order_index')
			final_samples = samples.filter(status__iexact='final').order_by('order_index')

			# Serialize
			scheduled_serialized = ProductSampleSerializer(scheduled_samples, many=True).data
			in_progress_serialized = ProductSampleSerializer(in_progress_samples, many=True).data
			retouching_serialized = ProductSampleSerializer(retouching_samples, many=True).data
			revisions_serialized = ProductSampleSerializer(revisions_samples, many=True).data
			final_serialized = ProductSampleSerializer(final_samples, many=True).data

			return Response({
				"status_code": 200,
				"status_message": "Fetched successfully",
				"data": {
					"scheduled": scheduled_serialized,
					"in_progress": in_progress_serialized,
					"retouching": retouching_serialized,
					"revisions": revisions_serialized,
					"final": final_serialized,
				}
			}, status=status.HTTP_200_OK)

		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)



class GetAssignUsers(APIView):
	def get(self,request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message':str(e)},status=status.HTTP_401_UNAUTHORIZED)
			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'user not found'}, status=status.HTTP_404_NOT_FOUND)
			 # Fetch all users except the one making the request
			other_users = EndUser.objects.exclude(Q(id=user_obj.id)|Q(is_client=True)|Q(is_admin=True))

			serialized_users = EndUserSerializer(other_users, many=True)

			return Response({'users': serialized_users.data}, status=status.HTTP_200_OK)
		except Exception as e:
			return Response({'message':str(e)},status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class AddProduct(APIView):
	def post(self, request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message':str(e)},status=status.HTTP_401_UNAUTHORIZED)
			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'user not found'}, status=status.HTTP_404_NOT_FOUND)
			data = request.data
			tm_article_number = data.get('tm_article_number')
			
			# Check if product with this tm_article_number already exists
			if not tm_article_number:
				return Response({'message': 'tm_article_number is required'}, status=status.HTTP_400_BAD_REQUEST)
			
			existing_product = ProductSample.objects.filter(tm_article_number=tm_article_number).first()
			if existing_product:
				return Response({
					'message': 'Product with this article number already exists',
					'product_id': existing_product.id,
					'tm_article_number': existing_product.tm_article_number
				}, status=status.HTTP_400_BAD_REQUEST)
			
			job_number = generate_job_number()
			product_sample = ProductSample.objects.create(
				job_number = job_number,
				product_line = data.get('product_line'),
				subfamily = data.get('subfamily'),
				tm_article_number = data.get('tm_article_number'),
				style_name = data.get('style_name'),
				og_style_name = data.get('og_style_name'),
				color_name = data.get('color_name'),
				season = data.get('season'),
				par_date = data.get('par_date'),
				number_of_samples_received = data.get('number_of_samples_received'),
				marketing_sample_estimated_date = data.get('marketing_sample_estimated_date'),
				rep_sms_photoshop_comments = data.get('rep_sms_photoshop_comments'),
				location = data.get('location'),
				sample_pass_off_date = data.get('sample_pass_off_date'),
				lionshead_received_date = data.get('lionshead_received_date'),
				flatlay_shoot_date = data.get('flatlay_shoot_date'),
				flatlay_shoot = data.get('flatlay_shoot'),
				on_model_shoot_date = data.get('on_model_shoot_date'),
				on_models_shot = data.get('on_models_shot'),
				sample_return_date = data.get('sample_return_date'),
				is_product_shot = data.get('is_product_shot', False),
				source = data.get('source'),
				status = data.get('status'),
				dropbox_url = data.get('dropbox_url'),
				last_updated_datetime = data.get('last_updated_datetime'),
				updated_by = data.get('updated_by'),
			)

			# === Add product to collection if collection_id is provided ===
			collection_id = data.get('collection_id')
			if collection_id:
				try:
					# Check if product is already in any collection
					existing_collection_product = AddCollectionProduct.objects.filter(product=product_sample).first()
					if existing_collection_product:
						existing_collection = existing_collection_product.collection
						return Response({
							'message': f'Product is already added to collection: {existing_collection.collection_name}',
							'existing_collection_id': existing_collection.id,
							'existing_collection_name': existing_collection.collection_name
						}, status=status.HTTP_400_BAD_REQUEST)
					
					collection_obj = CollectionsProduct.objects.filter(id=collection_id).first()
					if collection_obj:
						AddCollectionProduct.objects.create(collection=collection_obj, product=product_sample)
					else:
						# Collection not found, but don't fail the request
						print(f"Warning: Collection with id {collection_id} not found")
				except Exception as e:
					# Log error but don't fail the request
					print(f"Error adding product to collection: {str(e)}")
			# === End collection assignment ===

			return Response({
				'message': 'Product sample added successfully',
				'id': product_sample.id
			}, status=status.HTTP_201_CREATED)



		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


		

class DeleteProductByArticle(APIView):
	def delete(self, request, tm_article_number):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)

			product = ProductSample.objects.filter(tm_article_number=tm_article_number).first()
			if not product:
				return Response({'message': f'Product with article number {tm_article_number} not found'}, 
								status=status.HTTP_404_NOT_FOUND)

			if product.nas_folder_name:
				try:
					ssh = paramiko.SSHClient()
					ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
					ssh.connect(hostname, port=port, username=username, password=password, look_for_keys=False)
					folder_path = f"{remote_base_path}/{product.nas_folder_name}"
					ssh.exec_command(f'rm -rf "{folder_path}"')
				except Exception as e:
					print(f"Failed to delete NAS folder: {e}")
				finally:
					if ssh.get_transport() and ssh.get_transport().is_active():
						ssh.close()

			if product.dropbox_url:
				try:
					dropbox_folder_path = f"/{product.nas_folder_name}"
					dbx.files_delete_v2(dropbox_folder_path)
				except Exception as e:
					print(f"Failed to delete Dropbox folder: {e}")

			product.delete()

			return Response({
				'status': 200,
				'message': f'Product with article number {tm_article_number} deleted successfully.'
			}, status=status.HTTP_200_OK)

		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)



class AssignUser(APIView):
	def post(self, request):
		try:
			# ✅ 1. Authenticate user
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)

			# ✅ 2. Handle job numbers (string or list)
			job_numbers = request.data.get('job_number')
			if not job_numbers:
				return Response({'message': 'job_number is required'}, status=status.HTTP_400_BAD_REQUEST)

			if isinstance(job_numbers, str):
				job_numbers = [job_numbers]
			elif not isinstance(job_numbers, list):
				return Response({'message': 'job_number must be a string or a list of strings'}, status=status.HTTP_400_BAD_REQUEST)

			# ✅ 3. Handle watchers (int, str, or list)
			watchers = request.data.get('watchers', [])
			if isinstance(watchers, (int, str)):
				watchers = [int(watchers)]
			elif not isinstance(watchers, list):
				return Response({'message': 'watchers should be a list or a valid user ID'}, status=status.HTTP_400_BAD_REQUEST)

			# Remove duplicates
			watchers = list(set([int(wid) for wid in watchers]))

			# ✅ 4. Query valid watcher users
			watcher_users = EndUser.objects.filter(id__in=watchers)
			valid_watcher_ids = set(watcher_users.values_list('id', flat=True))

			if not valid_watcher_ids:
				return Response({'message': 'No valid watchers found'}, status=status.HTTP_404_NOT_FOUND)

			# ✅ 5. Query ProductSamples for all given job numbers
			product_samples = ProductSample.objects.filter(job_number__in=job_numbers)
			if not product_samples.exists():
				return Response({'message': 'No ProductSample found for the given job_number(s)'}, status=status.HTTP_404_NOT_FOUND)

			# ✅ Optional shoot date
			shoot_date_time = request.data.get('shoot_date_time')

			created_watchers = []

			# ✅ 6. Loop through product samples
			for product_sample in product_samples:
				if shoot_date_time:
					product_sample.shoot_date_time = shoot_date_time

				# Assign each watcher
				for watcher in watcher_users:
					shoot, created = Shoots.objects.update_or_create(
						product=product_sample,
						user=watcher,
						defaults={'is_watcher': True}
					)
					created_watchers.append(shoot.id)

					# Mark user as retoucher
					watcher.is_retoucher = True
					watcher.save(update_fields=['is_retoucher'])

					# Send watcher notification email (safely)
					try:
						context = {
							'user_name': watcher.name,
							'job_number': product_sample.job_number,
							'product_line': product_sample.product_line or "N/A",
							'assigned_date': datetime.now().strftime("%B %d, %Y"),
						}

						subject = "You're Invited for Retouching a Product – Lionshead Studios"
						html_content = render_to_string('retoucher_email.html', context)
						text_content = strip_tags(html_content)

						email = EmailMultiAlternatives(
							subject=subject,
							body=text_content,
							from_email=settings.EMAIL_HOST_USER,
							to=[watcher.email]
						)
						email.attach_alternative(html_content, "text/html")
						email.send()
					except Exception as email_error:
						# Log or print for debugging; do not interrupt process
						print(f"Email sending failed for watcher {watcher.id}: {email_error}")

				# Update product sample status once per product
				product_sample.status = "Assigned"
				product_sample.save(update_fields=['status', 'shoot_date_time'] if shoot_date_time else ['status'])

			# ✅ 7. Final response
			return Response({
				'message': 'Retouchers assigned successfully.',
				'watchers_added': list(valid_watcher_ids),
				'new_shoots': created_watchers,
				'job_numbers_processed': job_numbers,
			}, status=status.HTTP_200_OK)

		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)




class GetUserActivity(APIView):
	def get(self,request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)
			activity_obj = UserActivity.objects.all().order_by('-id')
			all_Data = []
			for activity in activity_obj:
				all_data = {
					'message':activity.message,
					'start_date':activity.start_date,
					'type':activity.type,
					'job_number':activity.job_number,

				}
				all_Data.append(all_data)
			return Response({'data': all_Data}, status=status.HTTP_200_OK)
		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)



class UpdateOrderIndex(APIView):
	def post(self, request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)

			job_list = request.data
			if not isinstance(job_list, list):
				return Response({'message': 'Invalid data format. Expected a list.'}, status=status.HTTP_400_BAD_REQUEST)

			for job in job_list:
				job_number = job.get("job_number")
				order_index = job.get("order_index")
				status_value = job.get("status")

				if not job_number:
					continue

				sample = ProductSample.objects.filter(job_number=job_number).first()
				if sample:
					changes = []

					if sample.order_index != order_index:
						changes.append(f"order index from {sample.order_index} → {order_index}")
						sample.order_index = order_index

					if sample.status != status_value:
						changes.append(f"status from '{sample.status}' → '{status_value}'")
						sample.status = status_value

					if changes:
						sample.updated_by = user_obj.email
						sample.last_updated_datetime = timezone.now().strftime('%Y-%m-%d %H:%M:%S')
						sample.save()

						# Log update by the person who made the change
						changer_identity = user_obj.name or user_obj.email
						update_message = (
							f"{changer_identity} updated sample '{sample.style_name or job_number}': "
							+ ", ".join(changes)
						)

						# Log own action
						UserActivity.objects.create(
							user=user_obj,
							message=update_message,
							is_read=False,
							type="sample_update",
							end_date=timezone.now()
						)

						# Notify all users assigned to the job (including watchers)
						related_shoots = Shoots.objects.filter(product=sample).select_related('user')
						for shoot in related_shoots:
							if shoot.user and shoot.user != user_obj:
								notify_msg = (
									f"Sample '{sample.style_name or job_number}' was updated by {changer_identity}: "
									+ ", ".join(changes)
								)
								UserActivity.objects.create(
									user=shoot.user,
									message=notify_msg,
									is_read=False,
									type="sample_notification",
									end_date=timezone.now()
								)

			return Response({'message': 'Order index and status updated successfully.'}, status=status.HTTP_200_OK)

		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
		

# Set your OpenAI API key securely
openai.api_key = settings.OPEN_AI_KEY
 
class SendMonthlyRecap(APIView):
	help = "Send AI-generated monthly activity summary emails"

	def get(self, request):
		today = now().date()
		# Calculate the previous month date range
		first_day_of_current_month = today.replace(day=1)
		last_month_end = first_day_of_current_month - timedelta(days=1)
		last_month_start = last_month_end.replace(day=1)

		users = EndUser.objects.filter(is_admin = True)

		for user in users:
			# Fetch all activities from the previous month
			activities = UserActivity.objects.filter(
				start_date__date__gte=last_month_start,
				start_date__date__lte=last_month_end
			).order_by('start_date')
			if not activities.exists():
				continue

			# Prepare raw activity text
			activity_lines = [
				f"[{a.start_date.strftime('%d %b %H:%M')}] {a.message}" for a in activities
			]
			joined_activities = "\n".join(activity_lines)

			# AI Summary prompt
			prompt = f"""You are an assistant summarizing monthly work logs for an email newsletter.
			Here's the activity log for the month of {last_month_start.strftime('%B %Y')}:

			{joined_activities}

			Write a friendly, concise 5-6 sentence summary highlighting key activities and achievements of the month."""

			try:
				response = openai.ChatCompletion.create(
					model="gpt-4",
					messages=[
						{"role": "system", "content": "You are a helpful assistant that summarizes monthly work activity."},
						{"role": "user", "content": prompt}
					],
					temperature=0.7,
				)

				summary = response['choices'][0]['message']['content'].strip()
			except Exception as e:
				summary = "We're unable to generate a monthly summary. Here's a raw list of activities:\n" + joined_activities

			# Render HTML template
			html_content = render_to_string("monthly_recap.html", {
				"user": user,
				"month": last_month_start.strftime('%B %Y'),
				"summary": summary,
				"activities": activities,
			})

			subject = f"📅 Monthly Recap – {last_month_start.strftime('%B %Y')}"
			email = EmailMultiAlternatives(
				subject=subject,
				body=summary,  # Plain text fallback
				from_email=settings.EMAIL_HOST_USER,
				to=["developer@esferasoft.com"]
			)
			email.attach_alternative(html_content, "text/html")
			email.send()

		return Response({'message': 'Monthly recap sent successfully.'}, status=status.HTTP_200_OK)

	
class DashboardSummary(APIView):
	def get(self, request):
		try:
			uid = authenticated(request)
		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

		user_obj = EndUser.objects.filter(id=uid).first()
		if not user_obj:
			return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)

		total_product_count = ProductSample.objects.all().count()
		unschedule_products = ProductSample.objects.filter(status = "unschedule").count()
		review_completed_products = ProductSample.objects.filter(status = "Review Success").count()
		assigned_products = ProductSample.objects.filter(status = "Assigned").count()
		retouching_done_products = ProductSample.objects.filter(status = "Retouching Done").count()
		total_retoucher =  EndUser.objects.filter(is_retoucher = True).count()
		total_collections = CollectionsProduct.objects.all().count()
		total_clients =  EndUser.objects.filter(is_client = True).count()
		retouchers = (
			EndUser.objects.filter(is_retoucher=True)
			.annotate(month=TruncMonth('created_at'))
			.values('month')
			.annotate(count=Count('id'))
			.order_by('month')
		)

		# Get monthly counts for clients
		clients = (
			EndUser.objects.filter(is_client=True)
			.annotate(month=TruncMonth('created_at'))
			.values('month')
			.annotate(count=Count('id'))
			.order_by('month')
		)
		def format_data(queryset):
			return {item['month'].strftime('%Y-%m'): item['count'] for item in queryset}

		retoucher_data = format_data(retouchers)
		client_data = format_data(clients)

		# Merge months for consistent x-axis
		all_months = sorted(set(retoucher_data.keys()) | set(client_data.keys()))

		client_retoucher_data = {
			'months': all_months,
			'retouchers': [retoucher_data.get(month, 0) for month in all_months],
			'clients': [client_data.get(month, 0) for month in all_months],
		}
		# Get monthly counts for products
		products = (
			ProductSample.objects
			.annotate(month=TruncMonth('start_date'))
			.values('month')
			.annotate(count=Count('id'))
			.order_by('month')
		)

		def format_data(queryset):
			return {
				item['month'].strftime('%Y-%m'): item['count']
				for item in queryset
			}

		product_data = format_data(products)

		# Prepare data for the graph
		months = sorted(product_data.keys())
		product_counts = [product_data.get(month, 0) for month in months]

		products_graph_data = {
			'months': months,
			'products_uploaded': product_counts,
		}
		return Response({
			'message': 'Dashboard summary retrieved successfully.',
			"total_product_count": total_product_count,
			"unschedule_products": unschedule_products,
			"review_completed_products": review_completed_products,
			"assigned_products": assigned_products,
			"retouching_done_products":retouching_done_products,
			"client_retoucher_data":client_retoucher_data,
			"total_retoucher":total_retoucher,
			"total_clients":total_clients,
			"total_collections":total_collections,
			"product_graph":products_graph_data,
		}, status=status.HTTP_200_OK)

 
class BulkUpdateProducts(APIView):
	def post(self, request):
		try:
			uid = authenticated(request)
		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

		user_obj = EndUser.objects.filter(id=uid).first()
		if not user_obj:
			return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)

		job_ids = request.data.get('job_ids', [])

		# Fields to update
		update_fields = {
			'product_line': request.data.get('product_line'),
			'subfamily': request.data.get('subfamily'),
			'tm_article_number': request.data.get('tm_article_number'),
			'style_name': request.data.get('style_name'),
			'og_style_name': request.data.get('og_style_name'),
			'color_name': request.data.get('color_name'),
			'season': request.data.get('season'),
			'par_date': request.data.get('par_date'),
			'number_of_samples_received': request.data.get('number_of_samples_received'),
			'marketing_sample_estimated_date': request.data.get('marketing_sample_estimated_date'),
			'rep_sms_photoshop_comments': request.data.get('rep_sms_photoshop_comments'),
			'location': request.data.get('location'),
			'sample_pass_off_date': request.data.get('sample_pass_off_date'),
			'lionshead_received_date': request.data.get('lionshead_received_date'),
			'flatlay_shoot_date': request.data.get('flatlay_shoot_date'),
			'flatlay_shoot': request.data.get('flatlay_shoot'),
			'on_model_shoot_date': request.data.get('on_model_shoot_date'),
			'on_models_shot': request.data.get('on_models_shot'),
			'sample_return_date': request.data.get('sample_return_date'),
			'is_product_shot': request.data.get('is_product_shot'),
			'source': request.data.get('source'),
			'status': request.data.get('status'),
			'sample_status': request.data.get('sample_status'),
			'shoot_date_time': request.data.get('shoot_date_time'),
			'style_number': request.data.get('style_number'),
			'sku_new_co': request.data.get('sku_new_co'),
			'style_new_co': request.data.get('style_new_co'),
			'last_updated_datetime': datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
			'updated_by': user_obj.name
		}

		updated_jobs = []
		updates_per_job = {}

		for job in job_ids:
			pro_obj = ProductSample.objects.filter(job_number=job).first()
			if pro_obj:
				updated_fields_detail = []

				for field, value in update_fields.items():
					if value is not None:
						original_value = getattr(pro_obj, field)
						if str(original_value) != str(value):
							updated_fields_detail.append({
								'field': humanize_field_name(field),
								'from': original_value,
								'to': value
							})
							setattr(pro_obj, field, value)

				if updated_fields_detail:
					pro_obj.save()
					updated_jobs.append(job)
					updates_per_job[job] = updated_fields_detail

					# Activity message
					field_changes_text = ", ".join(
						f"{change['field']}: '{change['from']}' → '{change['to']}'"
						for change in updated_fields_detail
					)

					UserActivity.objects.create(
						message=f"Job {job} updated by {user_obj.name}. Fields changed: {field_changes_text}",
						is_read=False,
						type="BulkProductUpdate",
						job_number = pro_obj.tm_article_number,
					)
	 
					Notifications.objects.create(
						message=f"Job {job} updated by {user_obj.name}. Fields changed: {field_changes_text}",
						is_read=False,
						type="BulkProductUpdate",
						user = user_obj,
					)

		# Email to all users
		users = EndUser.objects.filter(is_client = False)
		for user in users:
			# Dynamic subject line
			if len(updated_jobs) == 1:
				subject = f"Update: Job #{updated_jobs[0]} Details Modified – Lionshead Studios"
			else:
				jobs_list = ", ".join(str(job) for job in updated_jobs)	
				subject = f"Update: Job Numbers #{jobs_list} Details Modified – Lionshead Studios"
			from_email = settings.EMAIL_HOST_USER
			to_email = user.email

			html_content = render_to_string(
				"product_shot_updated.html",
				{
					'user_name': user.name,
					'updates_per_job': updates_per_job,
					'current_year': datetime.now().year
				}
			)

			msg = EmailMultiAlternatives(subject, "", from_email, [to_email])
			msg.attach_alternative(html_content, "text/html")
			msg.send()

		return Response({
			'message': f'{len(updated_jobs)} job(s) updated successfully.',
			'updated_jobs': updated_jobs
		}, status=status.HTTP_200_OK)
		



class AssignProductReview(APIView):
	def post(self, request):
		# Step 1: Authenticate user
		try:
			uid = authenticated(request)
		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

		user_obj = EndUser.objects.filter(id=uid).first()
		if not user_obj:
			return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)

		# Step 2: Get job_ids and client_emails
		job_ids = request.data.get('job_ids')
		client_emails = request.data.get('client_emails')

		if not isinstance(job_ids, list) or not job_ids:
			return Response({'message': 'job_ids must be a non-empty list'}, status=status.HTTP_400_BAD_REQUEST)

		if not isinstance(client_emails, list) or not client_emails:
			return Response({'message': 'client_emails must be a non-empty list'}, status=status.HTTP_400_BAD_REQUEST)

		results = []

		for job_id in job_ids:
			pro_obj = ProductSample.objects.filter(job_number=job_id).first()
			if not pro_obj:
				results.append({'job_id': job_id, 'status': 'failed', 'reason': 'Invalid product ID'})
				continue

			for client_email in client_emails:
				try:
					# Check or create client user
					client_user = EndUser.objects.filter(email=client_email).first()
					created = False
					raw_password = None

					if not client_user:
						raw_password = ''.join(random.choices(string.ascii_letters + string.digits, k=10))
						hashed_password = handler.hash(raw_password)
						client_user = EndUser.objects.create(
							email=client_email,
							is_client=True,
							password=hashed_password
						)
						created = True

						# Send email with credentials
						try:
							subject = "You're Invited to Review a Product – Lionshead Studios"
							from_email = settings.EMAIL_HOST_USER
							to_email = [client_email]

							text_content = f"""
							You've been invited to review a product.

							Email: {client_email}
							Password: {raw_password}

							Log in at: https://lionshead.tidera.ai/userauth/login
							"""

							html_content = render_to_string("invite_review.html", {
								'client_email': client_email,
								'raw_password': raw_password,
								'login_url': 'https://lionshead.tidera.ai/userauth/login',
								'company_name': 'The Lionshead Studios',
								'year': timezone.now().year
							})

							msg = EmailMultiAlternatives(subject, text_content, from_email, to_email)
							msg.attach_alternative(html_content, "text/html")
							msg.send()
						except Exception as email_error:
							results.append({
								'job_id': job_id,
								'client_email': client_email,
								'status': 'partial_success',
								'reason': f'User created, but email failed: {str(email_error)}'
							})
							continue

					# Create review assignment if not already assigned
					assignment_exists = InReviewProductData.objects.filter(client=client_user, product=pro_obj).exists()
					if not assignment_exists:
						InReviewProductData.objects.create(
							client=client_user,
							product=pro_obj,
							assign_date=timezone.now(),
							start_date=timezone.now()
						)
						pro_obj.is_client_review = True
						pro_obj.save()

					results.append({
						'job_id': job_id,
						'client_email': client_email,
						'status': 'success',
						'new_user': created
					})

				except Exception as e:
					results.append({
						'job_id': job_id,
						'client_email': client_email,
						'status': 'failed',
						'reason': str(e)
					})

		return Response({
			'message': 'Review assignment process completed.',
			'results': results
		}, status=status.HTTP_200_OK)
		
		

class ClientLogin(APIView):
	def post(self,request):
		try:
			data = request.data
			email = data.get('email').strip().lower()
			password = data.get('password')
			if not email:
				return Response({"message":'email is required'},status=status.HTTP_400_BAD_REQUEST) 
			if not password:
				return Response({"message":'password is required'},status=status.HTTP_400_BAD_REQUEST)  
			user  = EndUser.objects.filter(email=email,is_client = True ).count()
			if user == 0:
				return Response({"message":'This email does not exist in our database, please register'},status=status.HTTP_404_NOT_FOUND)
			
			userObj  = EndUser.objects.filter(email=email).first()

			userObj_delete  = EndUser.objects.filter(email=email,end_date__isnull = False).first()
			check_password = userObj.password
			check = handler.verify(password,check_password)
		
			if check:
				refresh_token = RefreshToken.for_user(userObj)
				allData={'id':userObj.id,
					'name':userObj.name,
					'email':userObj.email,
					'is_password_reset':userObj.is_password_reset,

				   }
				if userObj.is_linked_logined == False:
					userObj.is_linked_logined = True
					userObj.save()
					UserActivity.objects.create(message ="Client with email"+ userObj.email + " has logged into the product review portal.",type='client_login')
				tokens={
					'refresh': str(refresh_token),
					'access': str(refresh_token.access_token),
				}
				return Response({"message":'Login Successfully','data':allData,'tokens':tokens})
			else:
				return Response({"message":'Invalid password'},status=status.HTTP_400_BAD_REQUEST)
		except Exception as e:
			return Response({"message":str(e)},status=status.HTTP_500_INTERNAL_SERVER_ERROR)
		
		
class UpdatePassword(APIView):
	def post(self,request):
		try:
			uid = authenticated(request)
		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

		user_obj = EndUser.objects.filter(id=uid,is_client = True).first()
		if not user_obj:
			return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)
		
		
		new_password = request.data.get('new_password')
		if not new_password:
			return Response({'message': 'new_password is required'}, status=status.HTTP_404_NOT_FOUND)
		
		gen_password = handler.hash(new_password)
		user_obj.password = gen_password
		user_obj.is_password_reset = True
		user_obj.save()
		refresh_token = RefreshToken.for_user(user_obj)
		allData={'id':user_obj.id,
					'name':user_obj.name,
					'email':user_obj.email,
					'is_password_reset':user_obj.is_password_reset,
			}
		tokens={
				'refresh': str(refresh_token),
				'access': str(refresh_token.access_token),
			}
		return Response({"message":'Password Updated Successfully','data':allData,'tokens':tokens})

	
	
class MyAssignedReview(APIView):
	def get(self, request):
		try:
			uid = authenticated(request)
		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

		user_obj = EndUser.objects.filter(id=uid, is_client=True).first()
		if not user_obj:
			return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)

		# Base queryset with month/year annotation
		products = ProductSample.objects.filter(
			inreviewproductdata__client=user_obj,
			ready_for_client_review=True
		).annotate(
			month=ExtractMonth('inreviewproductdata__assign_date'),
			year=ExtractYear('inreviewproductdata__assign_date')
		).distinct()

		# Search functionality
		search_query = request.query_params.get('search', None)
		if search_query:
			search_fields = Q()
			fields_to_search = [
				'job_number', 'product_line', 'subfamily',
				'tm_article_number', 'style_name', 'og_style_name',
				'color_name', 'season', 'nas_folder_name'
			]
			for field in fields_to_search:
				search_fields |= Q(**{f"{field}__icontains": search_query})
			products = products.filter(search_fields)

		# Organize by month/year
		products_by_month = defaultdict(list)
		for product in products:
			month_name = datetime.strptime(str(product.month), '%m').strftime('%B')
			month_year = f"{month_name} {product.year}"
			products_by_month[month_year].append(product)

		# Accept sort order for months (ascending or descending)
		month_sort_order = request.query_params.get('month_sort_order', 'desc')  # Default is 'desc'

		# Prepare response
		response_data = []
		sorted_months = sorted(
			products_by_month.items(),
			key=lambda x: (x[0].split()[1], x[0].split()[0]),  # Sort by year then month
			reverse=(month_sort_order == 'desc')  # Reverse based on 'desc' or 'asc'
		)

		for month_year, products in sorted_months:
			serializer = ProductSampleSerializerClient(products, many=True)
			response_data.append({
				'month_year': month_year,
				'products': serializer.data,
				'count': len(products)
			})

		return Response(response_data, status=status.HTTP_200_OK)

class MyAssignedReviewDetails(APIView):
	def get(self,request):
		try:
			uid = authenticated(request)  # Your custom auth function
		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

		user_obj = EndUser.objects.filter(id=uid, is_client=True).first()
		if not user_obj:
			return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)
		job_id = request.GET.get('job_id')
		if not job_id:
			return Response({'message': 'job_id not found'}, status=status.HTTP_404_NOT_FOUND)
			
		try:
			product = ProductSample.objects.get(job_number=job_id)
		except ProductSample.DoesNotExist:
			return Response({'error': 'Product not found'}, status=status.HTTP_404_NOT_FOUND)
		nas_files = []
		# Get collection for the product
		collection_product = AddCollectionProduct.objects.filter(product=product).first()
		if collection_product and collection_product.collection:
			collection_name = collection_product.collection.collection_name
			ssh = paramiko.SSHClient()
			ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

			try:
				ssh.connect(hostname, port=port, username=username, password=password, look_for_keys=False)
				sftp = ssh.open_sftp()

				# Fetch from collection's Client Originals folder
				nas_folder_path = f"{remote_base_path}/{collection_name}/Client Originals"
				try:
					files = sftp.listdir(nas_folder_path)
				except FileNotFoundError:
					files = []

				# Filter images by article number pattern (e.g., L12101_01.png, L12101_02.png)
				image_files = filter_images_by_article_number(files, product.tm_article_number)

				# Local destination
				local_dir = os.path.join(settings.MEDIA_ROOT, 'nas_images', product.nas_folder_name or collection_name)
				os.makedirs(local_dir, exist_ok=True)

				for filename in image_files:
					remote_path = f"{nas_folder_path}/{filename}"
					local_path = os.path.join(local_dir, filename)

					# Skip if already exists
					if not os.path.exists(local_path):
						sftp.get(remote_path, local_path)

					# Return media URL
					media_url = f"{settings.MEDIA_URL}nas_images/{product.nas_folder_name or collection_name}/{filename}"
					nas_files.append(media_url)

			except Exception as e:
				return Response({'error': f'Failed to retrieve NAS files: {str(e)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
			finally:
				try:
					sftp.close()
					ssh.close()
				except:
					pass
 

		serializer = ProductSampleSerializer(product)
		response_data = serializer.data
		response_data["nas_files"] = nas_files
		return Response(response_data, status=status.HTTP_200_OK)






class AddComment(APIView):
	def post(self,request):
		try:
			uid = authenticated(request)  # Your custom auth function
		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

		user_obj = EndUser.objects.filter(id=uid).first()
		if not user_obj:
			return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)

		job_number = request.data.get('job_number')
		if not job_number:
			return Response({'message': 'job_number is required'}, status=status.HTTP_404_NOT_FOUND)

		product_obj = ProductSample.objects.filter(job_number = job_number ).first()
  
		comment_text = request.data.get('comment_text')
		comment_image = request.data.get('comment_image')
		is_comment_image = request.data.get('is_comment_image')
		mark_image_data = request.data.get('mark_image_data')
  
		ReviewDataComments.objects.create(product = product_obj,user = user_obj,comment_text=comment_text, comment_image=comment_image,is_comment_image = is_comment_image,mark_image_data=mark_image_data)
  
  
		   # Create UserActivity to log the comment posting activity
		if mark_image_data:
			UserActivity.objects.create(
				message=f"Client {user_obj.email} marked the review on the image for product {product_obj.job_number}",
				is_read=False, 
				type="comment_posted",
				job_number = product_obj.tm_article_number,
			)
			ProductSample.objects.filter(job_number=product_obj.job_number).update(
				notification_count=F('notification_count') + 1
			)
			Notifications.objects.create(
				message=f"Client {user_obj.email} marked the review on the image for product {product_obj.job_number}",
				is_read=False, 
				type="comment_posted",
				job_number = product_obj.tm_article_number,
				user = user_obj,
			)

		else:

			UserActivity.objects.create(
				message=f"Client {user_obj.email} posted a comment on product {product_obj.job_number}",
				is_read=False, 
				type="comment_posted",
				job_number = product_obj.tm_article_number,
			)
	
			ProductSample.objects.filter(job_number=product_obj.job_number).update(
				notification_count=F('notification_count') + 1
			)
			Notifications.objects.create(
				message=f"Client {user_obj.email} posted a comment on product {product_obj.job_number}",
				is_read=False, 
				type="comment_posted",
				job_number = product_obj.tm_article_number,
				user = user_obj,
			)
	
  
			send_comment_notification_email.delay(
						user_obj.email,
						product_obj.job_number,
						comment_text,
						comment_image
					)

  
  

		return Response({"message":'Posted successfully'},status=status.HTTP_200_OK)


class ClientUpdateProfile(APIView):
	def get(self, request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message':str(e)},status=status.HTTP_401_UNAUTHORIZED)
			user_obj = EndUser.objects.filter(id=uid,is_client = True).first()
			if not user_obj:
				return Response({'message': 'user not found'}, status=status.HTTP_404_NOT_FOUND)
			all_data = {
				'id':user_obj.id,
				'name':user_obj.name,
				'email':user_obj.email,
				'phone_number':user_obj.phone_number,
				'image':user_obj.image,
				'is_password_reset':user_obj.is_password_reset,
				'is_client':user_obj.is_client,

			}
			return Response({'status_code':status.HTTP_200_OK,'status_message':'Fetched Successfully','data':all_data})
		except Exception as e:
			return Response({'message':str(e)},status=status.HTTP_500_INTERNAL_SERVER_ERROR)
		

	def post(self,request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message':str(e)},status=status.HTTP_401_UNAUTHORIZED)
			user_obj = EndUser.objects.filter(id=uid,is_client = True).first()
			if not user_obj:
				return Response({'message': 'user not found'}, status=status.HTTP_404_NOT_FOUND)
			name = request.data.get('name')
			if not name:
				return Response({'status_code':status.HTTP_400_BAD_REQUEST,'status_message':'name is required'})
			email = request.data.get('email')
			if not email:
				return Response({'status_code':status.HTTP_400_BAD_REQUEST,'status_message':'email is required'})
			
			check_email = EndUser.objects.filter(email= email).exclude(id=user_obj.id)
			if check_email:
				return Response({'status_code':status.HTTP_400_BAD_REQUEST,'status_message':'This email is already associated with some account.'})
			
			phone_number = request.data.get('phone_number')
			if not phone_number:
				return Response({'status_code':status.HTTP_400_BAD_REQUEST,'status_message':'phone_number is required'})
			
			
			check_number = EndUser.objects.filter(phone_number = phone_number).exclude(id=user_obj.id)
			if check_number:
				return Response({'status_code':status.HTTP_400_BAD_REQUEST,'status_message':'This phone number is already associated with some account.'})
			
			image = request.data.get('image')

			user_obj.name = name
			user_obj.email = email
			user_obj.phone_number = phone_number
			user_obj.image = image
			user_obj.save()
			all_data = {
				'id':user_obj.id,
				'name':user_obj.name,
				'email':user_obj.email,
				'phone_number':user_obj.phone_number,
				'image':user_obj.image,
				'is_client':user_obj.is_client,
				'is_password_reset':user_obj.is_password_reset,

			}
			return Response({'status_code':status.HTTP_200_OK,'status_message':'Profile updated successfully','data':all_data})
		except Exception as e:
			return Response({'message':str(e)},status=status.HTTP_500_INTERNAL_SERVER_ERROR)

class GetComments(APIView):
	def get(self,request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message':str(e)},status=status.HTTP_401_UNAUTHORIZED)
			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'user not found'}, status=status.HTTP_404_NOT_FOUND)
			product_id = request.GET.get('product_id')
			if not product_id:
				return Response({'message': 'product_id is required'}, status=status.HTTP_404_NOT_FOUND)
			comments = ReviewDataComments.objects.filter(product_id=product_id)

			# Prepare the response data
			comment_data = []
			for comment in comments:
				comment_info = {
					'comment_text': comment.comment_text,
					'comment_image': comment.comment_image,
					'is_comment_image':comment.is_comment_image,
					'mark_image_data':comment.mark_image_data,
					'start_date': comment.start_date,
					'end_date': comment.end_date,
					'user': {
						'name': comment.user.name,
						'email': comment.user.email,
						'phone_number': comment.user.phone_number,
						'image': comment.user.image,
						'is_retoucher':comment.user.is_retoucher,
						'is_admin':comment.user.is_admin,
						'is_client':comment.user.is_client,
	  
					}
				}
				comment_data.append(comment_info)
			
			# If no comments found
			if not comment_data:
				return Response({'message': 'No comments found for this product'}, status=status.HTTP_200_OK)

			return Response({'comments': comment_data}, status=status.HTTP_200_OK)
			
		except Exception as e:
			return Response({'message':str(e)},status=status.HTTP_500_INTERNAL_SERVER_ERROR)



class GetImageData(APIView):
	def get(self, request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'user not found'}, status=status.HTTP_404_NOT_FOUND)

			product_id = request.GET.get('product_id')
			if not product_id:
				return Response({'message': 'product_id is required'}, status=status.HTTP_404_NOT_FOUND)

			comments = (
				ReviewDataComments.objects
				.filter(product_id=product_id)
				.exclude(mark_image_data__isnull=True)
				.exclude(mark_image_data__exact='')
				.last()
			)

			if not comments:
				return Response({'image_data': ''}, status=status.HTTP_200_OK)

			comment_info = {
				'mark_image_data': comments.mark_image_data,
				'start_date': comments.start_date,
				'end_date': comments.end_date,
			}

			return Response({'image_data': comment_info}, status=status.HTTP_200_OK)

		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)




class ReadCommentNotifications(APIView):
	def post(self,request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message':str(e)},status=status.HTTP_401_UNAUTHORIZED)
			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'user not found'}, status=status.HTTP_404_NOT_FOUND)
			job_number =  request.data.get('job_number')
			if not job_number:
				return Response({'message': 'job_number is required'}, status=status.HTTP_404_NOT_FOUND)
			prod_obj = ProductSample.objects.filter(tm_article_number  = job_number).first()
			prod_obj.notification_count = 0
			prod_obj.save()

			return Response({'status_code':status.HTTP_200_OK,'status_message':'Read successfully'})
		except Exception as e:
			return Response({'message':str(e)},status=status.HTTP_500_INTERNAL_SERVER_ERROR)



class GetNotifications(APIView):
	def get(self,request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)
			activity_obj = Notifications.objects.all().order_by('-id')
			all_Data = []
			for activity in activity_obj:
				all_data = {
					'id':activity.id,
					'message':activity.message,
					'start_date':activity.start_date,
					'type':activity.type,
					'is_read':activity.is_read,
					'job_number':activity.job_number,
					'user_name':activity.user.name,
					'user_email':activity.user.email,
	 

				}
				all_Data.append(all_data)
	

			return Response({'data': all_Data}, status=status.HTTP_200_OK)
		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class ReadNotification(APIView):
	def post(self,request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)
			job_number  = request.data.get('job_number')
			for job in list(job_number):
				job_obj =  Notifications.objects.filter(id = job).first()
				job_obj.is_read = True
				job_obj.save()

			return Response({'status_code':status.HTTP_200_OK,'status_message':'Read successfully'})
		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class GetNotificationCount(APIView):
	def get(self,request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)
			not_obj = Notifications.objects.filter(is_read = False).order_by('-id')[:5]
			unread_count = Notifications.objects.filter(is_read = False).count()
			all_Data = []
			for activity in not_obj:
				all_data = {
					'id':activity.id,
					'message':activity.message,
					'start_date':activity.start_date,
					'type':activity.type,
					'is_read':activity.is_read,
					'job_number':activity.job_number,
					'user_name':activity.user.name,
					'user_email':activity.user.email,
				}
				all_Data.append(all_data)

			return Response({'status_code':status.HTTP_200_OK,'status_message':'Read successfully','data' :all_Data,'unread_count':unread_count})
		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

class ArchiveUnarchiveProduct(APIView):
	def post(self,request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)
			article_number = request.data.get('article_number')
			if not article_number:
				return Response({'message': 'article_number is required'}, status=status.HTTP_404_NOT_FOUND)
			archive_status = request.data.get('archive_status')
			if not archive_status:
				return Response({'message': 'archive_status is required'}, status=status.HTTP_404_NOT_FOUND)
			for article in list(article_number):
				prod_obj = ProductSample.objects.filter(tm_article_number = article).first()
				if archive_status == "true":
					prod_obj.is_archive = True
				else:
					prod_obj.is_archive = False

				prod_obj.save()
			return Response({'status_code':status.HTTP_200_OK,'status_message':'Status updated successfully'})

		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class DropUndropProduct(APIView):
	def post(self,request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)
			article_number =  request.data.get('article_number')
			if not article_number:
				return Response({'message': 'article_number is required'}, status=status.HTTP_404_NOT_FOUND)
			drop_status =  request.data.get('drop_status')
			if not drop_status:
				return Response({'message': 'drop_status is required'}, status=status.HTTP_404_NOT_FOUND)
			for article in list(article_number):
				prod_obj = ProductSample.objects.filter(tm_article_number  = article).first()
				if drop_status == "true":
					prod_obj.is_dropped = True
				else:
					prod_obj.is_dropped = False

				prod_obj.save()
			return Response({'status_code':status.HTTP_200_OK,'status_message':'Status updated successfully'})

		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)



# class readyClientReview(APIView):
# 	def post(self,request):
# 		try:
# 			try:
# 				uid = authenticated(request)
# 			except Exception as e:
# 				return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

# 			user_obj = EndUser.objects.filter(id=uid).first()
# 			if not user_obj:
# 				return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)
# 			article_number =  request.data.get('article_number')
# 			key_status = request.data.get('key_status')
# 			if not key_status:
# 				return Response({'message': 'key_status is required'}, status=status.HTTP_404_NOT_FOUND)
# 			if not article_number:
# 				return Response({'message': 'article_number is required'}, status=status.HTTP_404_NOT_FOUND)
# 			ProductSample.objects.filter(tm_article_number  = article_number).update(ready_for_client_review = key_status)
# 			return Response({'status_code':status.HTTP_200_OK,'status_message':'Status updated successfully'})
# 		except Exception as e:
# 			return Response({'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)



class readyClientReview(APIView):
	def post(self, request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)
			
			article_numbers = request.data.get('article_number')
			key_status = request.data.get('key_status')
			
			if not key_status:
				return Response({'message': 'key_status is required'}, status=status.HTTP_400_BAD_REQUEST)
			if not article_numbers:
				return Response({'message': 'article_number is required'}, status=status.HTTP_400_BAD_REQUEST)
			
			# Ensure article_numbers is a list (even if single value is passed)
			if not isinstance(article_numbers, list):
				article_numbers = [article_numbers]
				
			# Update all articles in the list
			ProductSample.objects.filter(tm_article_number__in=article_numbers).update(ready_for_client_review=key_status)
			
			return Response({
				'status_code': status.HTTP_200_OK,
				'status_message': 'Status updated successfully',
				'updated_count': len(article_numbers)
			})
		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class retoucherJobListing(APIView):
	def get(self, request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message':str(e)},status=status.HTTP_401_UNAUTHORIZED)
			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'user not found'}, status=status.HTTP_404_NOT_FOUND)
			search = request.query_params.get('search', None)
			samples = ProductSample.objects.filter(
				shoots__user=user_obj  # Filter ProductSample by related Shoots where user is the logged-in user
			).distinct()  # Use distinct() to avoid duplicates if multiple shoots are assigned to a product
		
	
			if search:
				samples = samples.filter(
					Q(product_line__icontains=search) |
					Q(subfamily__icontains=search) |
					Q(tm_article_number__icontains=search) |
					Q(style_name__icontains=search) |
					Q(og_style_name__icontains=search) |
					Q(color_name__icontains=search) |
					Q(season__icontains=search) |
					Q(job_number__icontains=search) |
					Q(location__icontains=search)
				)
			# Sorting functionality
			sort_by = request.query_params.get('sort_by', None)
			sort_order = request.query_params.get('sort_order', 'asc')  # Default to 'asc'

			# If 'sort_by' is provided, apply sorting
			if sort_by:
				if sort_order == 'asc':
					samples = samples.order_by(sort_by)
				else:  # 'desc'
					samples = samples.order_by(f'-{sort_by}')
			paginator = CustomProductPagination()
			result_page = paginator.paginate_queryset(samples, request)
			serializer = ProductSampleSerializer(result_page, many=True)
   
			response_data = {
				'sort_by': sort_by,
				'sort_order': sort_order,
				'results': serializer.data,
			}

			return paginator.get_paginated_response(response_data)

		except Exception as e:
			return Response({'message':str(e)},status=status.HTTP_500_INTERNAL_SERVER_ERROR)



class myCredSettings(APIView):
	def get(self,request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message':str(e)},status=status.HTTP_401_UNAUTHORIZED)
			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'user not found'}, status=status.HTTP_404_NOT_FOUND)

			cred_obj =  CredSettings.objects.all().first()
   
			all_data = {
				'id':cred_obj.id,
				'dropbox_app_key':cred_obj.dropbox_app_key,
				'dropbox_app_secret':cred_obj.dropbox_app_secret,
				'dropbox_refresh_token':cred_obj.dropbox_refresh_token,
				'nas_hostname':cred_obj.nas_hostname,
				'nas_port_no':cred_obj.nas_port_no,
				'nas_username':cred_obj.nas_username,
				'nas_password':cred_obj.nas_password,
				'nas_remote_base_path':cred_obj.nas_remote_base_path,
	
			}
			return Response({'status_code':status.HTTP_200_OK,'status_message':'Read successfully','data' :all_data})
		except Exception as e:
			return Response({'message':str(e)},status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class logoutUser(APIView):
	def post(self, request):
		try:
			# Clear the cookies by setting them to expire immediately
			response = Response({"message": "Logged out successfully"}, status=status.HTTP_200_OK)
			response.delete_cookie('access_token')  # Delete the access_token cookie
			response.delete_cookie('refresh_token')  # Delete the refresh_token cookie
			
			return response
		
		except Exception as e:
			return Response({"message": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class createCollection(APIView):
	def get(self,request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message':str(e)},status=status.HTTP_401_UNAUTHORIZED)
			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'user not found'}, status=status.HTTP_404_NOT_FOUND)

			collection_obj = CollectionsProduct.objects.all().order_by('-id')
			all_Data = []
			for col in collection_obj:
				all_data = {
					'id':col.id,
					'collection_name':col.collection_name,
					'start_date':col.start_date,
		
				}

				all_Data.append(all_data)
			return Response({'status_code':status.HTTP_200_OK,'status_message':'Fetched successfully','data' :all_Data})

		except Exception as e:
			return Response({"message": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

	def post(self,request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message':str(e)},status=status.HTTP_401_UNAUTHORIZED)
			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'user not found'}, status=status.HTTP_404_NOT_FOUND)
			collection_name = request.data.get('collection_name')

			if not collection_name:
				return Response({'message': 'user not found'}, status=status.HTTP_404_NOT_FOUND)

			existing_collection = CollectionsProduct.objects.filter(collection_name=collection_name).first()
			if existing_collection:
				return Response({'message': 'Collection with this name already exists'}, status=status.HTTP_404_NOT_FOUND)

			
			collection = CollectionsProduct.objects.create(collection_name = collection_name)

			# === Create NAS folder structure for collection ===
			# Use the same pattern as AddProduct - direct module-level variables
			ssh = paramiko.SSHClient()
			ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
			nas_error = None
			try:
				ssh.connect(hostname, port=port, username=username, password=password, look_for_keys=False)
				
				# Create collection folder path
				collection_folder_path = f"{remote_base_path}/{collection_name}"
				
				# Create subfolders
				subfolders = [
					"_PSD Working",
					"Client Originals",
					"Clipping Paths",
					"Digital Proofs",
					"Feedback",
					"Finals",
					"Trash later"
				]
				
				# Create main collection folder
				stdin, stdout, stderr = ssh.exec_command(f'mkdir -p "{collection_folder_path}"')
				exit_status = stdout.channel.recv_exit_status()
				if exit_status != 0:
					error_msg = stderr.read().decode()
					nas_error = f'Failed to create collection folder: {error_msg}'
					print(f"NAS Error: {nas_error}")
				else:
					print(f"Successfully created collection folder: {collection_folder_path}")
				
				# Create all subfolders
				for subfolder in subfolders:
					subfolder_path = f"{collection_folder_path}/{subfolder}"
					stdin, stdout, stderr = ssh.exec_command(f'mkdir -p "{subfolder_path}"')
					exit_status = stdout.channel.recv_exit_status()
					if exit_status != 0:
						error_msg = stderr.read().decode()
						print(f"Warning: Failed to create subfolder {subfolder}: {error_msg}")
					else:
						print(f"Successfully created subfolder: {subfolder_path}")
				
			except paramiko.AuthenticationException as e:
				nas_error = f'NAS SSH Authentication failed: {str(e)}'
				print(f"NAS Error: {nas_error}")
			except paramiko.SSHException as e:
				nas_error = f'NAS SSH connection error: {str(e)}'
				print(f"NAS Error: {nas_error}")
			except Exception as e:
				nas_error = f'Failed to create NAS folder: {str(e)}'
				print(f"NAS Error: {nas_error}")
				import traceback
				traceback.print_exc()
			finally:
				try:
					if ssh.get_transport() and ssh.get_transport().is_active():
						ssh.close()
				except:
					pass
			# === End of NAS folder creation ===
			# Note: Collection is already created in database, so we return success even if NAS fails
			# The NAS error is logged but doesn't prevent collection creation

			response_data = {
				'status_code': status.HTTP_200_OK,
				'status_message': 'Created successfully'
			}
			
			# Include NAS error in response if it occurred (for debugging, but don't fail the request)
			if nas_error:
				response_data['nas_warning'] = nas_error
			
			return Response(response_data)

		except Exception as e:
			return Response({"message": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)



class addToCollection(APIView):
	def get(self, request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)
			
			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'user not found'}, status=status.HTTP_404_NOT_FOUND)
			
			collection_id = request.query_params.get('collection_id')
			
			if collection_id:
				# Get all products in the specified collection
				collection_products = AddCollectionProduct.objects.filter(
					collection_id=collection_id
				).select_related('product')
				
				if not collection_products.exists():
					return Response({'message': 'No products found in this collection'}, 
									status=status.HTTP_404_NOT_FOUND)
				
				# Prepare response data
				products_data = []
				for item in collection_products:
					product = item.product
					products_data.append({
						'product_id': product.tm_article_number,
						'product_line': product.product_line, 
						'start_date': item.start_date,
						'end_date': item.end_date
					})
				
				collection_obj = CollectionsProduct.objects.filter(id=collection_id).first()
				return Response({
					'status_code': status.HTTP_200_OK,
					'collection_id': collection_id,
					'collection_name': collection_obj.collection_name if collection_obj else 'Unknown',
					'products': products_data
				})
						
			else:
				return Response({'message': 'collection_id  is required'}, 
								status=status.HTTP_400_BAD_REQUEST)
			
		except Exception as e:
			return Response({"message": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


	def post(self,request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message':str(e)},status=status.HTTP_401_UNAUTHORIZED)
			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'user not found'}, status=status.HTTP_404_NOT_FOUND)
			product_id = request.data.get('product_id')
			if not product_id:
				return Response({'message': 'product_id is required'}, status=status.HTTP_404_NOT_FOUND)
			product_obj = ProductSample.objects.filter(tm_article_number = product_id).first()
			if not product_obj:
				return Response({'message': 'No product found'}, status=status.HTTP_404_NOT_FOUND)
		
			collection_id = request.data.get('collection_id')
			if not collection_id:
				return Response({'message': 'collection_id is required'}, status=status.HTTP_404_NOT_FOUND)
			collection_obj = CollectionsProduct.objects.filter(id = collection_id).first()
			if not collection_obj:
				return Response({'message': 'No collection found'}, status=status.HTTP_404_NOT_FOUND)
			
			# Check if product is already in any collection
			existing_collection_product = AddCollectionProduct.objects.filter(product=product_obj).first()
			if existing_collection_product:
				existing_collection = existing_collection_product.collection
				# Check if it's the same collection
				if existing_collection.id == collection_obj.id:
					return Response({'message': 'Product already exists in this collection'}, status=status.HTTP_400_BAD_REQUEST)
				else:
					return Response({
						'message': f'Product is already added to another collection: {existing_collection.collection_name}',
						'existing_collection_id': existing_collection.id,
						'existing_collection_name': existing_collection.collection_name
					}, status=status.HTTP_400_BAD_REQUEST)

			AddCollectionProduct.objects.create(collection = collection_obj,product = product_obj)
			return Response({'status_code':status.HTTP_200_OK,'status_message':'Created successfully'})
		except Exception as e:
			return Response({"message": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)



class deleteCollection(APIView):
	def post(self, request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'user not found'}, status=status.HTTP_404_NOT_FOUND)

			collection_id = request.data.get('collection_id')
			if not collection_id:
				return Response({'message': 'collection_id is required'}, status=status.HTTP_400_BAD_REQUEST)

			collection_obj = CollectionsProduct.objects.filter(id=collection_id).first()
			if not collection_obj:
				return Response({'message': 'Collection not found'}, status=status.HTTP_404_NOT_FOUND)

			AddCollectionProduct.objects.filter(collection=collection_obj).delete()

			collection_obj.delete()

			return Response({
				'status_code': status.HTTP_200_OK,
				'status_message': 'Collection and its products deleted successfully'
			})

		except Exception as e:
			return Response({"message": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)



class changeClientViewStatus(APIView):
	def post(self,request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

			review_id = request.data.get('review_id')
			if not review_id:	
				return Response({'message': 'review_id id required'}, status=status.HTTP_404_NOT_FOUND)
			
			review_obj = InReviewProductData.objects.filter(id=review_id).first()
			review_obj.is_viewed = True
			review_obj.save()
			UserActivity.objects.create(
				message=f"Job {review_obj.product.job_number} has been viewed by the client.",
				is_read=False,
				type="ReviewViewed",
				job_number = review_obj.product.tm_article_number,
			)
	 
			return Response({
				'status_code': status.HTTP_200_OK,
				'status_message': 'Status updated successfully'
			})
		except Exception as e:
			return Response({"message": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class addWatchers(APIView):
	def post(self, request):
		try:
			emails = request.data.get('emails')
			if not emails:    
				return Response({'message': 'emails are required'}, status=status.HTTP_400_BAD_REQUEST)
			
			product_id = request.data.get('product_id')
			if not product_id:    
				return Response({'message': 'product_id is required'}, status=status.HTTP_400_BAD_REQUEST)

			product_obj = ProductSample.objects.filter(tm_article_number=product_id).first()
			if not product_obj:
				return Response({'message': 'No product found'}, status=status.HTTP_404_NOT_FOUND)
			
			context = {
				'job_number': product_obj.job_number,
				'product_line': product_obj.product_line or "N/A",
			}
			
			for email in list(emails):
				# Create watcher record
				Watchers.objects.create(email=email, product=product_obj)
				
				# Send email
				subject = "You're assigned as a watcher for Product – Lionshead Studios"
				html_content = render_to_string('watcher_email.html', context)
				text_content = strip_tags(html_content)

				email_msg = EmailMultiAlternatives(
					subject=subject,
					body=text_content,
					from_email=settings.EMAIL_HOST_USER,
					to=[email]
				)
				email_msg.attach_alternative(html_content, "text/html")
				email_msg.send()
			
			return Response({
				'status': 'success',
				'message': 'Watchers added and notified successfully',
				'status_code': status.HTTP_200_OK
			})
			
		except Exception as e:
			return Response({
				'status': 'error',
				'message': str(e)
			}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
		


def handle_nas_and_dropbox(product_obj):
	"""Handles NAS file copying and Dropbox upload."""
	nas_files = []

	if not product_obj.nas_folder_name:
		return None

	ssh = paramiko.SSHClient()
	ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())

	try:
		ssh.connect(hostname, port=port, username=username, password=password, look_for_keys=False)
		sftp = ssh.open_sftp()

		nas_folder_path = f"/Projects_In_Progress_LH2/{product_obj.nas_folder_name}/Raw Data"
		final_folder_path = f"/Projects_In_Progress_LH2/{product_obj.nas_folder_name}/Final Files"

		try:
			files = sftp.listdir(nas_folder_path)
		except FileNotFoundError:
			return None

		image_extensions = ['.jpg', '.jpeg', '.png', '.webp']
		image_files = [f for f in files if any(f.lower().endswith(ext) for ext in image_extensions)]

		try:
			sftp.mkdir(final_folder_path)
		except IOError:
			pass  # Folder might already exist

		for filename in image_files:
			remote_path = f"{nas_folder_path}/{filename}"
			final_remote_path = f"{final_folder_path}/{filename}"

			with sftp.open(remote_path, 'rb') as source_file:
				file_data = source_file.read()

			with sftp.open(final_remote_path, 'wb') as destination_file:
				destination_file.write(file_data)

			# Upload to Dropbox
			user_id = settings.DROPBOX_USER_ID
			dbx._session.headers.update({'Dropbox-API-Select-User': user_id})
			dropbox_path = f"/{product_obj.nas_folder_name}/Final Files/{filename}"

			dbx.files_upload(file_data, dropbox_path, mute=True)

		# Create Dropbox folder link
		dropbox_folder_path = f"/{product_obj.nas_folder_name}/Final Files"
		folder_url = dbx.sharing_create_shared_link_with_settings(dropbox_folder_path).url
		product_obj.dropbox_url = folder_url
		product_obj.save()

	except Exception as e:
		print(f"NAS/Dropbox Error: {str(e)}")
	finally:
		try:
			sftp.close()
			ssh.close()
		except:
			pass

class bulkApprove(APIView):
	def post(self, request):
		try:
			# Authenticate user
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)

			# Input
			product_status = request.data.get('status')
			job_numbers = request.data.get('job_numbers')

			if not product_status:
				return Response({'message': 'status is required'}, status=status.HTTP_400_BAD_REQUEST)
			if not job_numbers or not isinstance(job_numbers, list):
				return Response({'message': 'job_numbers must be a list'}, status=status.HTTP_400_BAD_REQUEST)

			updated_jobs = []
			not_found_jobs = []

			for job_number in job_numbers:
				product_obj = ProductSample.objects.filter(job_number=job_number).first()
				if not product_obj:
					not_found_jobs.append(job_number)
					continue

				product_obj.status = product_status
				product_obj.save()
				updated_jobs.append(job_number)

				# Only handle NAS/Dropbox for Review Success
				if product_status == "Review Success":
					handle_nas_and_dropbox(product_obj)

			# Email content
			if updated_jobs:
				admin_users = EndUser.objects.filter(is_admin=True)

				subject = "Success: Client Review Completed – Lionshead Studios"
				template = 'review_complete_email.html'

				context = {
					'job_numbers': updated_jobs,
					'product_status': product_status,
					'url': f"https://lionshead.tidera.ai/admin/products/{job_number}"
				}
				html_content = render_to_string(template, context)
				text_content = strip_tags(html_content)

				for admin in admin_users:
					email = EmailMultiAlternatives(
						subject=subject,
						body=text_content,
						from_email=settings.EMAIL_HOST_USER,
						to=[admin.email]
					)
					email.attach_alternative(html_content, "text/html")
					email.send()

			return Response({
				'status_code': status.HTTP_200_OK,
				'status_message': f'Status updated for {len(updated_jobs)} product(s)',
				'updated_job_numbers': updated_jobs,
				'not_found_job_numbers': not_found_jobs
			})

		except Exception as e:
			  return Response({'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
		

class getAllUsers(APIView):
	def get(self, request):
		try:
			# Authenticate user
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)

			# Base queryset
			all_user = EndUser.objects.exclude(id = user_obj.id).order_by('-id')

			# Filtering by type
			user_type = request.GET.get('type')  # e.g. client, retoucher, admin
			if user_type == "client":
				all_user = all_user.filter(is_client=True)
			elif user_type == "retoucher":
				all_user = all_user.filter(is_retoucher=True)
			elif user_type == "admin":
				all_user = all_user.filter(is_admin=True)

			# Pagination params
			page = request.GET.get('page', 1)       
			per_page = request.GET.get('per_page', 10)  # default items per page = 10

			paginator = Paginator(all_user, per_page)
			current_page = paginator.get_page(page)

			all_Data = []
			for user in current_page:
				all_data = {
					'id': user.id,
					'name': user.name,
					'email': user.email,
					'phone_number': user.phone_number,
					'image': user.image,
					'last_login': user.last_login,
					'is_client': user.is_client,
					'is_retoucher': user.is_retoucher,
					'is_admin': user.is_admin,
				}
				all_Data.append(all_data)

			return Response({
				'status_code': status.HTTP_200_OK,
				'status_message': 'Success',
				'total_users': paginator.count,
				'total_pages': paginator.num_pages,
				'current_page': current_page.number,
				'per_page': int(per_page),
				'data': all_Data,
			})

		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
		


class deleteUser(APIView):
	def post(self, request):
		try:
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

			user_obj = EndUser.objects.filter(id=uid).first()
			if not user_obj:
				return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)
			user_id = request.data.get('user_id')
			if not user_id:
				return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)
					
			delete_user = EndUser.objects.filter(id=user_id).first()
			if not delete_user:
				return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)

			if delete_user.id == uid:
				return Response({'message': 'You cannot delete your own account'}, status=status.HTTP_400_BAD_REQUEST)

			delete_user.delete()

			return Response({
				'status_code': status.HTTP_200_OK,
				'status_message': f'User {user_id} deleted successfully'
			})

		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)




class createUser(APIView):
	def post(self, request):
		try:
			# Authenticate creator (optional: only admin can create users)
			try:
				uid = authenticated(request)
			except Exception as e:
				return Response({'message': str(e)}, status=status.HTTP_401_UNAUTHORIZED)

			creator = EndUser.objects.filter(id=uid).first()
			if not creator:
				return Response({'message': 'User not found'}, status=status.HTTP_404_NOT_FOUND)

			if not creator.is_admin:  # only admins allowed
				return Response({'message': 'Permission denied'}, status=status.HTTP_403_FORBIDDEN)

			# Extract data
			name = request.data.get("name")
			email = request.data.get("email").strip().replace(" ", "").lower()
			phone_number = request.data.get("phone_number")
			is_client = request.data.get("is_client", False)
			is_retoucher = request.data.get("is_retoucher", False)
			is_admin = request.data.get("is_admin", False)

			if not name or not email:
				return Response({'message': 'Name and Email are required'}, status=status.HTTP_400_BAD_REQUEST)

			# Check if email already exists
			if EndUser.objects.filter(email=email).exists():
				return Response({'message': 'Email already registered'}, status=status.HTTP_400_BAD_REQUEST)

			# Generate random password
			password = ''.join(random.choices(string.ascii_letters + string.digits, k=10))

			# Create user
			new_user = EndUser.objects.create(
				name=name,
				email=email,
				phone_number=phone_number,
				is_client=is_client,
				is_retoucher=is_retoucher,
				is_admin=is_admin,
				password=make_password(password)  # hash password
			)

			# Send password over email
			try:
				send_mail(
					subject="Your Account Credentials",
					message=f"Hello {name},\n\nYour account has been created.\nEmail: {email}\nPassword: {password}\n\nPlease change your password after login.",
					from_email=settings.EMAIL_HOST_USER,
					recipient_list=[email],
					fail_silently=False,
				)
			except Exception as mail_err:
				return Response({'message': f'User created but failed to send email: {str(mail_err)}'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

			return Response({
				'status_code': status.HTTP_201_CREATED,
				'status_message': 'User created successfully. Credentials sent via email.',
				'user_id': new_user.id
			})

		except Exception as e:
			return Response({'message': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


