from django.core.exceptions import ValidationError as DjangoValidationError
from .models import JpnSubPerf  # このインポート文をファイルの先頭に追加
from django.contrib.auth import get_user_model
User = get_user_model()
import traceback
from django.contrib.auth.hashers import make_password
from urllib.parse import unquote  # URLデコード用

import subprocess  # subprocessモジュールを追加
import tempfile  # tempfileモジュールを追加
import shutil    # shutilモジュールを追加

from django.contrib.auth.tokens import default_token_generator
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.utils.encoding import force_bytes, force_str

import requests 
from rest_framework import serializers
from django.db import IntegrityError
from django.urls import reverse
from .utils import S3Bucket, send_verification_email,send_invitation_email,send_team_join_email,send_reset_password_email
from django.conf import settings
import uuid
from rest_framework.exceptions import ValidationError as DRFValidationError

from django.db import transaction
from django.db.models import F,Sum,Q
from rest_framework import viewsets, permissions, status
from rest_framework.decorators import action
from rest_framework.response import Response
from django.shortcuts import get_object_or_404
from .models import Team, Member, CustomUser, NewCategory
from .serializers import TeamSerializer, MemberSerializer, CustomUserSerializer, TeamDetailSerializer,UserUpdateSerializer,UserRegistrationSerializer
from .permissions import IsTeamOwner,IsTeamOwnerOrMember

from curses.ascii import NUL
from django.core.serializers import serialize
from .models import GoalImages, Location2025, Location_line, Location_polygon, JpnAdminMainPerf, Useractions, GifuAreas, RogUser, CustomUser, UserTracks, GoalImages, CheckinImages, NewEvent,NewEvent2, Team, Category, NewCategory,Entry, Member, TempUser, EntryMember, GpsCheckin
from rest_framework import viewsets
from .serializers import LocationSerializer, Location_lineSerializer, Location_polygonSerializer, JPN_main_perfSerializer, LocationCatSerializer, UserSerializer, LoginUserSerializer, UseractionsSerializer, UserDestinationSerializer, GifuAreaSerializer, LocationEventNameSerializer, RogUserSerializer, UserTracksSerializer, ChangePasswordSerializer, GolaImageSerializer, CheckinImageSerializer, RegistrationSerializer, MemberWithUserSerializer,TempUserRegistrationSerializer, PasswordResetRequestSerializer, PasswordResetConfirmSerializer
from knox.models import AuthToken

from rest_framework import viewsets, generics, status
from rest_framework.response import Response
from rest_framework.parsers import JSONParser, MultiPartParser
from .serializers import LocationSerializer
from django.http import JsonResponse
from rest_framework.permissions import IsAuthenticated , AllowAny
# 
# AllowAny: 認証なしで誰でもアクセス可能
# IsAuthenticated: 認証済みユーザーのみアクセス可能
# IsAdminUser: 管理者のみアクセス可能
# IsAuthenticatedOrReadOnly: 読み取りは誰でも可能、書き込みは認証済みユーザーのみ可能

from django.contrib.gis.db.models import Extent, Union

from .serializers import TestSerialiser,NewEventSerializer,NewEvent2Serializer, TeamSerializer, NewCategorySerializer,CategorySerializer, EntrySerializer, MemberSerializer, TempUserSerializer, CustomUserSerializer,EntryMemberSerializer,MemberCreationSerializer,EntryCreationSerializer
from .models import TestModel
from django.shortcuts import get_object_or_404
from django.db.models import Max
from django.contrib.gis import geos
from django.db.models import Q

from rest_framework import permissions
from rest_framework.views import APIView
from rest_framework.decorators import api_view, permission_classes
from rest_framework.parsers import JSONParser, MultiPartParser
from django.views.decorators.csrf import csrf_exempt
from django.shortcuts import render
from .permissions import IsMemberOrTeamOwner

from django.utils.decorators import method_decorator
from django.utils.encoding import force_str

import logging
from datetime import datetime,timedelta
from django.core.cache import cache
import time

from django.contrib.gis.measure import D
from django.contrib.gis.geos import Point
from django.contrib.gis.db.models.functions import Distance

from django.utils.dateparse import parse_date
from django.utils import timezone

import csv
import io
from django.contrib import admin
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.urls import path
from django import forms
from .models import NewEvent2, CustomUser, Team, NewCategory, Entry, Member, TempUser
from django.core.mail import send_mail
from django.conf import settings
from django.db import transaction
from django.core.exceptions import ValidationError

import xlsxwriter
from io import BytesIO

from django.urls import get_resolver
import os
import json
from django.http import HttpResponse
from sumaexcel import SumasenExcel

logger = logging.getLogger(__name__)

@api_view(['PATCH'])
@permission_classes([IsAuthenticated])
def update_entry_status(request, entry_id):
    try:
        entry = Entry.objects.get(id=entry_id)
    except Entry.DoesNotExist:
        return Response({"error": "Entry not found"}, status=status.HTTP_404_NOT_FOUND)

    # エントリーの所有者またはチームのメンバーのみが更新可能
    if entry.owner != request.user and not entry.team.members.filter(user=request.user).exists():
        return Response({"error": "You don't have permission to update this entry"}, status=status.HTTP_403_FORBIDDEN)

    hasParticipated = request.data.get('hasParticipated')
    hasGoaled = request.data.get('hasGoaled')

    if hasParticipated is not None:
        entry.hasParticipated = hasParticipated
    if hasGoaled is not None:
        entry.hasGoaled = hasGoaled

    entry.save()
    serializer = EntrySerializer(entry)
    return Response(serializer.data)


def process_csv_upload(csv_file, event):
    decoded_file = csv_file.read().decode('utf-8')
    io_string = io.StringIO(decoded_file)
    reader = csv.reader(io_string)
    next(reader)  # ヘッダーをスキップ

    for row in reader:
        try:
            owner_email, owner_name, owner_grade, owner_gender, team_name, category_name, *member_data = row
            # ここでデータを処理し、必要なオブジェクトを作成します
            # 例: create_or_update_team(owner_email, owner_name, owner_grade, owner_gender, team_name, category_name, event)
            # 例: process_members(member_data, team)
        except ValidationError as e:
            # エラーハンドリング
            pass


class CSVUploadForm(forms.Form):
    event = forms.ModelChoiceField(queryset=NewEvent2.objects.all())
    csv_file = forms.FileField()

class NewEvent2Admin(admin.ModelAdmin):
    list_display = ['event_name', 'start_datetime', 'end_datetime', 'deadlineDateTime','public']
    
    def get_urls(self):
        urls = super().get_urls()
        my_urls = [
            path('csv-upload/', self.admin_site.admin_view(self.csv_upload_view), name='csv-upload'),
        ]
        return my_urls + urls

    def csv_upload_view(self, request):
        if request.method == 'POST':
            form = CSVUploadForm(request.POST, request.FILES)
            if form.is_valid():
                event = form.cleaned_data['event']
                csv_file = request.FILES['csv_file']
                csv_data = csv_file.read().decode('utf-8')
                csv_reader = csv.reader(io.StringIO(csv_data))
                next(csv_reader)  # ヘッダーをスキップ

                for row in csv_reader:
                    self.process_team(event, row)

                self.message_user(request, "CSV file has been processed successfully.")
                return HttpResponseRedirect("..")
        else:
            form = CSVUploadForm()

        context = {
            'form': form,
            'title': 'Upload CSV file',
        }
        return render(request, 'admin/csv_upload.html', context)

    @transaction.atomic
    def process_team(self, event, row):
        owner_data = row[:4]
        team_name = row[4]
        category_name = row[5]
        members_data = row[6:]

        owner = self.create_or_get_user(*owner_data)
        category = NewCategory.objects.get(category_name=category_name)
        team, _ = Team.objects.get_or_create(owner=owner, team_name=team_name, category=category)
        
        entry, _ = Entry.objects.get_or_create(
            team=team,
            event=event,
            category=category,
            owner=owner,
            date=event.start_datetime.date()
        )

        for i in range(0, len(members_data), 4):
            member_data = members_data[i:i+4]
            if member_data[0]:  # メールアドレスがある場合のみ処理
                member = self.create_or_get_user(*member_data)
                Member.objects.get_or_create(team=team, user=member)

    def create_or_get_user(self, email, name, grade, gender):
        is_dummy = False
        if not email:
            email = f"dummy_{uuid.uuid4()}@example.com"
            is_dummy = True

        user, created = CustomUser.objects.get_or_create(email=email)
        if created or is_dummy:
            user.firstname, user.lastname = name.split(' ', 1) if ' ' in name else (name, '')
            user.grade = grade
            user.gender = gender
            user.is_active = not is_dummy
            user.save()

            if not is_dummy:
                self.send_invitation_email(user)

        return user

    def send_invitation_email(self, user):
        subject = 'Invitation to join the event'
        message = f'Please click the following link to activate your account: {settings.SITE_URL}/activate/{user.id}/'
        send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, [user.email])


class LocationViewSet(viewsets.ModelViewSet):
    queryset=Location2025.objects.all()
    serializer_class=LocationSerializer
    filter_fields=[
        "event", "cp_name", "subcategory", "sub_loc_id", 
        "is_active", "hidden_location", "evaluation_value"
    ]

    def get_queryset(self):
        queryset = Location2025.objects.all()

        # リクエストパラメータの確認
        event_filter = self.request.query_params.get('event')
        subcategory_filter = self.request.query_params.get('subcategory')
        has_photos_filter = self.request.query_params.get('has_photos')
        has_videos_filter = self.request.query_params.get('has_videos')
        tags_filter = self.request.query_params.get('tags__contains')
        evaluation_filter = self.request.query_params.get('evaluation_value')
        hidden_filter = self.request.query_params.get('hidden_location')

        if event_filter:
            queryset = queryset.filter(event_id=event_filter)
            
        if subcategory_filter:
            queryset = queryset.filter(subcategory__icontains=subcategory_filter)
            
        if has_photos_filter == 'true':
            queryset = queryset.exclude(photos__isnull=True).exclude(photos='')
        elif has_photos_filter == 'false':
            queryset = queryset.filter(Q(photos__isnull=True) | Q(photos=''))
            
        if has_videos_filter == 'true':
            queryset = queryset.exclude(videos__isnull=True).exclude(videos='')
        elif has_videos_filter == 'false':
            queryset = queryset.filter(Q(videos__isnull=True) | Q(videos=''))
            
        if tags_filter:
            queryset = queryset.filter(tags__icontains=tags_filter)
            
        if evaluation_filter:
            queryset = queryset.filter(evaluation_value=evaluation_filter)
            
        if hidden_filter == 'true':
            queryset = queryset.filter(hidden_location=True)
        elif hidden_filter == 'false':
            queryset = queryset.filter(hidden_location=False)

        return queryset

    def list(self, request, *args, **kwargs):
        try:
            response = super().list(request, *args, **kwargs)
            return response
        except Exception as e:
            logger.error(f"Error in list method: {str(e)}", exc_info=True)
            raise

class Location_lineViewSet(viewsets.ModelViewSet):
    queryset=Location_line.objects.all()
    serializer_class=Location_lineSerializer


class Location_polygonViewSet(viewsets.ModelViewSet):
    queryset=Location_polygon.objects.all()
    serializer_class=Location_polygonSerializer


class Jpn_Main_PerfViewSet(viewsets.ModelViewSet):
    queryset=JpnAdminMainPerf.objects.filter(id=9)
    serializer_class=JPN_main_perfSerializer
    filter_fields = ["adm1_ja"]



class UserTracksViewSet(viewsets.ModelViewSet):    
    queryset = UserTracks.objects.all()
    serializer_class = UserTracksSerializer


@api_view(['PUT'])
@permission_classes([IsAuthenticated])
def update_user_info(request, user_id):
    try:
        user = CustomUser.objects.get(id=user_id)
    except CustomUser.DoesNotExist:
        return Response({"error": "User not found"}, status=status.HTTP_404_NOT_FOUND)

    if request.user.id != user_id:
        return Response({"error": "You don't have permission to update this user's information"}, status=status.HTTP_403_FORBIDDEN)

    data = request.data
    logger.debug(f"Received data for update: {data}")

    # CustomUserの更新可能なフィールドを指定
    updateable_fields = ['zekken_number', 'event_code', 'team_name', 'group']

    #for field in updateable_fields:
    #    if field in data:
    #        setattr(user, field, data[field])

    updated_fields = []
    for field in updateable_fields:
        if field in data:
            old_value = getattr(user, field)
            setattr(user, field, data[field])
            new_value = getattr(user, field)
            if old_value != new_value:
                updated_fields.append(f"{field}: {old_value} -> {new_value}")

    logger.debug(f"Fields to be updated: {updated_fields}")

    try:
        user.save()

        logger.info(f"User {user_id} updated. Changed fields: {', '.join(updated_fields)}")

        serializer = CustomUserSerializer(user)

        return Response(serializer.data, status=status.HTTP_200_OK)
    except Exception as e:
        logger.error(f"Error updating user {user_id}: {str(e)}")
        return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

@api_view(['PUT'])
@permission_classes([IsAuthenticated])
def update_user_detail(request, user_id):
    try:
        user = CustomUser.objects.get(id=user_id)
    except CustomUser.DoesNotExist:
        return Response({"error": "User not found"}, status=status.HTTP_404_NOT_FOUND)

    if request.user.id != user_id:
        return Response({"error": "You don't have permission to update this user's information"}, status=status.HTTP_403_FORBIDDEN)
        
    data = request.data
    logger.debug(f"Received data for update: {data}")
    
    # CustomUserの更新可能なフィールドを指定
    updateable_fields = ['firstname', 'lastname', 'date_of_birth', 'female']
                
    #for field in updateable_fields:
    #    if field in data:
    #        setattr(user, field, data[field])
                
    updated_fields = []
    for field in updateable_fields:
        if field in data:
            old_value = getattr(user, field)
            setattr(user, field, data[field])
            new_value = getattr(user, field)
            if old_value != new_value:
                logger.debug(f"{field}: {old_value} -> {new_value}")
                updated_fields.append(f"{field}: {old_value} -> {new_value}")
            
    logger.debug(f"Fields to be updated: {updated_fields}")
            
    try:
        user.save()

        logger.info(f"User {user_id} updated. Changed fields: {', '.join(updated_fields)}")

        # 更新されたUserデータを使用して関連するMemberのデータを更新
        members = Member.objects.filter(user=user)
        for member in members:
            member.is_temporary = False
            member.save()

        serializer = CustomUserSerializer(user)

        return Response(serializer.data, status=status.HTTP_200_OK)
    except Exception as e:
        logger.error(f"Error updating user {user_id}: {str(e)}")
        return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)



def LocationsInPerf(request):
    perfecture = request.GET.get('perf')
    is_rog = request.GET.get('rog')
    cat = request.GET.get('cat')
    grp = request.GET.get('grp')
    perf_geom = JpnAdminMainPerf.objects.get(id=perfecture)
    
    if(cat):
        if is_rog:
            if grp:
                locs = Location2025.objects.filter(~Q(cp=0), geom__within=perf_geom.geom, category=cat, group__contains=grp)
            else:
                locs = Location2025.objects.filter(~Q(cp=0), geom__within=perf_geom.geom, category=cat)
        else:
            if grp:
                locs = Location2025.objects.filter(geom__within=perf_geom.geom, category=cat, group__contains=grp, location_name__contains='観光')
            else:
                locs = Location2025.objects.filter(geom__within=perf_geom.geom, category=cat, location_name__contains='観光')
    else:
        if is_rog:
            if grp:
                locs = Location2025.objects.filter(~Q(cp=0), geom__within=perf_geom.geom, group__contains=grp)
            else:
                locs = Location2025.objects.filter(~Q(cp=0), geom__within=perf_geom.geom)
        else:
            if grp:
                locs = Location2025.objects.filter(geom__within=perf_geom.geom, group__contains=grp, location_name__contains='観光') 
            else:
                locs = Location2025.objects.filter(geom__within=perf_geom.geom, location_name__contains='観光') 

    serializer = LocationSerializer(locs, many=True)
    return JsonResponse(serializer.data, safe=False)

def LocationsInSubPerf(request):
    subperfecture = request.GET.get('subperf')
    is_rog = request.GET.get('rog')
    cat = request.GET.get('cat')
    grp = request.GET.get('grp')
    perf_geom = JpnSubPerf.objects.get(id=subperfecture)
    
    if(cat):
        if is_rog:
            if grp:
                locs = Location2025.objects.filter(~Q(cp=0), geom__within=perf_geom.geom, category=cat, group__contains=grp)
            else:
                locs = Location2025.objects.filter(~Q(cp=0), geom__within=perf_geom.geom, category=cat)
        else:
            if grp:
                locs = Location2025.objects.filter(geom__within=perf_geom.geom, category=cat, group__contains=grp, location_name__contains='観光')
            else:
                locs = Location2025.objects.filter(geom__within=perf_geom.geom, category=cat, location_name__contains='観光')
    else:
        if is_rog:
            if grp:
                locs = Location2025.objects.filter(~Q(cp=0), geom__within=perf_geom.geom, group__contains=grp)
            else:
                locs = Location2025.objects.filter(~Q(cp=0), geom__within=perf_geom.geom)
        else:
            locs = Location2025.objects.filter(geom__within=perf_geom.geom, location_name__contains='観光')
    serializer = LocationSerializer(locs, many=True)
    return JsonResponse(serializer.data, safe=False)

# この関数LocationInBoundは、地理的な範囲内にある特定の条件を満たす位置情報を検索し、結果をJSON形式で返すものです。主なロジックは以下の通りです：
#
# 1.リクエストパラメータの取得:
#   4つの緯度経度ペア（lat1/lon1からlat4/lon4）
#      カテゴリ（cat）
#      グループ（grp）
#      ROG（Region of Gaze）フラグ（is_rog）
#
# 2. 境界ポリゴンの作成:
#   4つの緯度経度ペアが全て提供された場合、それらを使用してジオメトリポリゴンを作成します。
#
# 3.位置情報のフィルタリング:
#   基本的に、ポリゴン内（geom__within=pl）にある位置情報を検索します。
#   イベント名がない（event_name__isnull=True）位置情報のみを対象とします。
#   カテゴリ、グループ、ROGフラグの有無に応じて、さらにフィルタリングを行います：
#       カテゴリが指定された場合、そのカテゴリに一致する位置情報のみを検索します。
#       ROGフラグがある場合、cp（おそらくcheck point）が0でない位置情報を検索します。
#       ROGフラグがない場合、location_nameに'観光'を含む位置情報のみを検索します。
#       グループが指定された場合、そのグループを含む位置情報のみを検索します。
#
# 4.結果の返却:
#   検索結果が120件を超える場合、"too_many_points"フラグを立てたJSONレスポンスを返します。
#   それ以外の場合、LocationSerializerを使用して位置情報をシリアライズし、JSONレスポンスとして返します。
#
# 5.エラーハンドリング:
#   必要な緯度経度パラメータが不足している場合、空のJSONオブジェクトを返します。
#
#   この関数は、主に観光関連の位置情報を特定の地理的範囲内で検索し、様々な条件でフィルタリングすることができます。ROGフラグの有無によって検索条件が変わるのが特徴的です。
#

def LocationInBound(request):
    logger.debug(f"Received request parameters: {request.GET}")

    lat = float(request.GET.get('la1'))
    lon = float(request.GET.get('ln1'))
    cat = request.GET.get('cat')
    grp = request.GET.get('grp')
    is_rog = request.GET.get('rog')

    logger.debug(f"Parsed parameters: lat={lat}, lon={lon}, "
                 f"cat={cat}, grp={grp}, is_rog={is_rog}")

    if lat is not None and lon is not None:
        current_point = Point(lon, lat, srid=4326)
        
        # 10km四方の領域を指定
        distance_from_point = 40000  # 中心から10km（半径）

        base_query = Location2025.objects.filter(
            geom__distance_lte=(current_point, D(m=distance_from_point))
        ).annotate(distance=Distance('geom', current_point)).order_by('distance')

        #if cat:
        #    base_query = base_query.filter(category=cat)

        #if is_rog:
        #base_query = base_query.filter(~Q(cp=0))
        #else:
        #    base_query = base_query.filter(location_name__contains='観光')

        if grp:
            base_query = base_query.filter(group__contains=grp)

        logger.debug(f"Final query: {base_query.query}")

        locs = base_query

        logger.debug(f"Number of locations found: {len(locs)}")

        print(f"====== {len(locs)} check points is loaded.. ======")

        if len(locs) > 200:
            return JsonResponse({"too_many_points": True}, safe=False, status=500)
        else:
            serializer = LocationSerializer(locs, many=True)
            return JsonResponse(serializer.data, safe=False)
    else:
        return JsonResponse({"error": "Latitude and longitude are required"}, safe=False, status=400)

def LocationInBound2(request):
    logger.debug(f"Received request parameters: {request.GET}")

    lat = float(request.GET.get('la1'))
    lon = float(request.GET.get('ln1'))
    cat = request.GET.get('cat')
    grp = request.GET.get('grp')
    is_rog = request.GET.get('rog')

    logger.debug(f"Parsed parameters: lat={lat}, lon={lon}, "
                 f"cat={cat}, grp={grp}, is_rog={is_rog}")

    if lat is not None and lon is not None:
        if grp:
            # grpがある場合、最初に絞り込む
            base_query = Location2025.objects.filter(group__contains=grp)
        else:
            current_point = Point(lon, lat, srid=4326)
            # 10km四方の領域を指定
            distance_from_point = 40000  # 中心から10km（半径）

            base_query = Location2025.objects.filter(
                geom__distance_lte=(current_point, D(m=distance_from_point))
            ).annotate(distance=Distance('geom', current_point)).order_by('distance')
        
            if grp:
                base_query = base_query.filter(group__contains=grp)

        logger.debug(f"Final query: {base_query.query}")
        locs = base_query
        logger.debug(f"Number of locations found: {len(locs)}")
        print(f"====== {len(locs)} check points is loaded.. ======")

        if len(locs) > 200:
            return JsonResponse({"too_many_points": True}, safe=False, status=500)
        else:
            serializer = LocationSerializer(locs, many=True)
            return JsonResponse(serializer.data, safe=False)

    else:
        return JsonResponse({"error": "Latitude and longitude are required"}, safe=False, status=400)



def LocationInBound_orig(request):
    logger.debug(f"Received request parameters: {request.GET}")

    lat1 = float(request.GET.get('la1'))
    lon1 = float(request.GET.get('ln1'))
    lat2 = float(request.GET.get('la2'))
    lon2 = float(request.GET.get('ln2'))
    lat3 = float(request.GET.get('la3'))
    lon3 = float(request.GET.get('ln3'))
    lat4 = float(request.GET.get('la4'))
    lon4 = float(request.GET.get('ln4'))
    cat = request.GET.get('cat')
    grp = request.GET.get('grp')

    is_rog = request.GET.get('rog')

    logger.debug(f"Parsed parameters: lat1={lat1}, lon1={lon1}, lat2={lat2}, lon2={lon2}, "
                 f"lat3={lat3}, lon3={lon3}, lat4={lat4}, lon4={lon4}, "
                 f"cat={cat}, grp={grp}, is_rog={is_rog}")

    print(f"=====  cat={cat}, grp={grp}, is_rog={is_rog}  =======")


    if(lat1 != None and lon1 != None and lat2 != None and lon2 != None and lat3 != None and lon3 != None and lat4 != None and lon4 != None):
        pl = geos.Polygon(((lon1, lat1), (lon2, lat2), (lon3, lat3), (lon4, lat4), (lon1, lat1)), srid=4326)
        logger.debug(f"Created polygon: {pl}")

        base_query = Location2025.objects.filter(geom__within=pl, event_name__isnull=True)
        
        if cat:
            base_query = base_query.filter(category=cat)
        
        if is_rog:
            base_query = base_query.filter(~Q(cp=0))
        else:
            base_query = base_query.filter(location_name__contains='観光')
        
        if grp:
            base_query = base_query.filter(group__contains=grp)

        logger.debug(f"Final query: {base_query.query}")
        
        locs = base_query

        logger.debug(f"Number of locations found: {len(locs)}")


        print(f"====== {len(locs)} check points is loaded.. ======")
        '''
        if(cat):
            if is_rog:
                if grp:
                    locs = Location2025.objects.filter(~Q(cp=0), geom__within=pl, category=cat, event_name__isnull=True, group__contains=grp)
                else:
                    locs = Location2025.objects.filter(~Q(cp=0), geom__within=pl, category=cat, event_name__isnull=True)
            else:
                if grp:
                    locs = Location2025.objects.filter(geom__within=pl, category=cat, event_name__isnull=True, group__contains=grp, location_name__contains='観光')
                else:
                    locs = Location2025.objects.filter(geom__within=pl, category=cat, event_name__isnull=True, location_name__contains='観光')
        else:
            if is_rog:
                if grp:
                    locs = Location2025.objects.filter(~Q(cp=0), geom__within=pl, event_name__isnull=True, group__contains=grp)
                else:
                    locs = Location2025.objects.filter(~Q(cp=0), geom__within=pl, event_name__isnull=True)
            else:
                if grp:
                    locs = Location2025.objects.filter(geom__within=pl, event_name__isnull=True, group__contains=grp, location_name__contains='観光')
                else:
                    locs = Location2025.objects.filter(geom__within=pl, event_name__isnull=True, location_name__contains='観光')
        '''

        if len(locs) > 200:
            return JsonResponse({"too_many_points": True}, safe=False, status=500)
        else:
            serializer = LocationSerializer(locs, many=True)
            return JsonResponse(serializer.data, safe=False)
    else:
        return JsonResponse({}, safe=False)



    
def SubInPerf(request):
    prefecture = request.GET.get('perf')
    perf_geom = JpnAdminMainPerf.objects.get(id=prefecture)
    sub = JpnAdminPerf.objects.filter(geom__within=perf_geom.geom)
    serializer = JPN_perfSerializer(sub, many=True)
    return JsonResponse(serializer.data, safe=False)


def SubPerfInMainPerf(request):
    area = request.GET.get('area')
    perf_geom = GifuAreas.objects.get(id=area)
    sub = JpnSubPerf.objects.filter(geom__contained=perf_geom.geom)
    #serializer = JPN_sub_perSerializer
    #sub = JpnAdminPerf.objects.filter(geom__within=perf_geom.geom)
    serializer = JPN_sub_perSerializer(sub, many=True)
    return JsonResponse(serializer.data, safe=False)

def GetAllGifuAreas(request):
    prefecture = request.GET.get('perf')
    perf_geom = JpnAdminMainPerf.objects.get(id=prefecture)
    sub = GifuAreas.objects.filter(geom__contained=perf_geom.geom)
    serializer = GifuAreaSerializer(sub, many=True)
    return JsonResponse(serializer.data, safe=False)


def ExtentForMainPerf(request):
    perf_id = request.GET.get('perf')
    perf = JpnAdminMainPerf.objects.get(id=perf_id)
    ext = perf.geom.extent
    # iata = serializers.serialize("json",ext)
    return JsonResponse(ext, safe=False)

@api_view(['POST',])
@permission_classes((IsAuthenticated,))
@csrf_exempt 
def ExtentForLocations(request):
    user = request.user
    ec = user.event_code
    print(user.event_code)
    logger.debug(f"ExtentForLocations called for user: {user.email}, event_code: {ec}")

    if not ec:
        logger.warning(f"ExtentForLocations: No event_code for user {user.email}")
        return JsonResponse({"error": "No event code associated with the user"}, status=status.HTTP_400_BAD_REQUEST)

    try:
        # event_codeに基づいてイベントを検索し、そのイベントに関連するLocation2025を取得
        from rog.models import NewEvent2
        from django.db.models import Q
        
        # event_codeでイベントを検索（event_nameに含まれる場合も考慮）
        events = NewEvent2.objects.filter(
            Q(event_code=ec) | 
            Q(event_name__icontains=ec)
        )
        
        if not events.exists():
            logger.warning(f"No events found for event_code: {ec}")
            return JsonResponse({"error": "No events found for the given event code"}, status=status.HTTP_404_NOT_FOUND)
        
        # 見つかったイベントに関連するLocation2025を取得
        locs = Location2025.objects.filter(event__in=events).aggregate(Extent('location'), Union('location'))

        if not locs['location__extent']:
            logger.info(f"No locations found for event_code: {ec}")
            return JsonResponse({"error": "No locations found for the given event code"}, status=status.HTTP_404_NOT_FOUND)

        logger.info(f"Successfully retrieved extent for event_code: {ec}")
        return JsonResponse(locs['location__extent'], safe=False)

    except Exception as e:
        logger.error(f"Error in ExtentForLocations: {str(e)}", exc_info=True)
        return JsonResponse({"error": "An error occurred while processing your request"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    #locs = Location2025.objects.filter(group__contains=ec).aggregate(Extent('geom'), Union('geom'))
    #return JsonResponse(locs['geom__extent'], safe=False)


def ExtentForSubPerf_old(request):
    sub_perf_id = request.GET.get('sub_perf')
    sub_perf = JpnSubPerf.objects.get(id=sub_perf_id)
    ext = sub_perf.geom.extent
    # iata = serializers.serialize("json",ext)
    return JsonResponse(ext, safe=False)
   
def ExtentForSubPerf(request):
    sub_perf_id = request.GET.get('sub_perf')
    if not sub_perf_id:
        return JsonResponse({"error": "sub_perf parameter is required"}, status=400)
    
    try:
        sub_perf = JpnSubPerf.objects.get(id=sub_perf_id)
        ext = sub_perf.geom.extent
        return JsonResponse(ext, safe=False)
    except JpnSubPerf.DoesNotExist:
        return JsonResponse({"error": "Specified sub_perf does not exist"}, status=404)
    except Exception as e:
        # テーブルが存在しない場合やその他のデータベースエラーをキャッチ
        return JsonResponse({"error": f"Service temporarily unavailable: {str(e)}"}, status=503)
  


def CatView(request):
    lat1 = float(request.GET.get('la1'))
    lon1 = float(request.GET.get('ln1'))
    lat2 = float(request.GET.get('la2'))
    lon2 = float(request.GET.get('ln2'))
    lat3 = float(request.GET.get('la3'))
    lon3 = float(request.GET.get('ln3'))
    lat4 = float(request.GET.get('la4'))
    lon4 = float(request.GET.get('ln4'))

    if(lat1 != None and lon1 != None and lat2 != None and lon2 != None and lat3 != None and lon3 != None and lat4 != None and lon4 != None):
        pl = geos.Polygon(((lon1, lat1), (lon2, lat2), (lon3, lat3), (lon4, lat4), (lon1, lat1)), srid=4326)
        #locs = Location2025.objects.filter(geom__within=pl)
        c = Location2025.objects.filter(geom__within=pl).values('category').distinct()
        serializer = LocationCatSerializer(c, many=True)
        return JsonResponse(serializer.data, safe=False)
    else:
        return null


    c = Location2025.objects.filter().values('category').distinct()
    serializer = LocationCatSerializer(c, many=True)
    return JsonResponse(serializer.data, safe=False)


def CatByCity(request):
    city = request.GET.get('city')
    if(city != None):
        cilt_polygon = JpnSubPerf.objects.filter(adm1_ja=city)
        cats = Location2025.objects.filter(geom__within=cilt_polygon[0].geom).values('category').distinct()
        serializer = LocationCatSerializer(cats, many=True)
        return JsonResponse(serializer.data, safe=False)
    else:
        return None



class RegistrationAPI(generics.GenericAPIView):
    #serializer_class = CreateUserSerializer
    serializer_class = UserRegistrationSerializer

    def post(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.save()
        return Response({
            "user": UserSerializer(user, context=self.get_serializer_context()).data,
            "token": AuthToken.objects.create(user)[1]
        })


class LoginView(APIView):
    def post(self, request):
        email = request.data.get('email')
        password = request.data.get('password')

        # デバッグコード
        user = CustomUser.objects.filter(email=email).first()
        if user:
            stored_hash = user.password
            print(f"Stored hashed password: {stored_hash}")
            is_valid = check_password(raw_password, stored_hash)
            print(f"Password is valid during login: {is_valid}")
        
        user = authenticate(request, username=email, password=raw_password)
        if user:
            token, _ = Token.objects.get_or_create(user=user)
            return Response({'token': token.key}, status=status.HTTP_200_OK)
        else:
            return Response({'error': 'Invalid credentials'}, status=status.HTTP_400_BAD_REQUEST)



        #user = authenticate(request, username=email, password=password)
        #if user:
        #    token, _ = Token.objects.get_or_create(user=user)
        #    return Response({'token': token.key}, status=status.HTTP_200_OK)
        #else:
        #    return Response({'error': 'Invalid credentials'}, status=status.HTTP_400_BAD_REQUEST)

class PasswordResetView(APIView):
    def post(self, request):
        email = request.data.get('email')
        user = CustomUser.objects.filter(email=email).first()
        if user:
            token = default_token_generator.make_token(user)
            uid = urlsafe_base64_encode(force_bytes(user.pk))
            reset_link = f"https://yourwebsite.com/reset-password/{uid}/{token}/"
            send_mail(
                'Password Reset',
                f'Click here to reset your password: {reset_link}',
                'noreply@yourwebsite.com',
                [email],
                fail_silently=False,
            )
            return Response({'message': 'Password reset email sent'}, status=status.HTTP_200_OK)
        else:
            return Response({'error': 'User not found'}, status=status.HTTP_400_BAD_REQUEST)




class LoginAPI(generics.GenericAPIView):

    serializer_class = LoginUserSerializer

    def post(self, request, *args, **kwargs):
        logger.info(f"Login attempt for identifier: {request.data.get('identifier', 'identifier not provided')}")
        logger.debug(f"Request data: {request.data}")

        # フロントエンドの 'identifier' フィールドを 'email' にマッピング
        data = request.data.copy()
        if 'identifier' in data and 'email' not in data:
            data['email'] = data['identifier']

        serializer = self.get_serializer(data=data)
        try:
            serializer.is_valid(raise_exception=True)
            user = serializer.validated_data
            logger.info(f"User {user.email} logged in successfully")

            # ユーザー情報をシリアライズ
            user_data = UserSerializer(user, context=self.get_serializer_context()).data

            # 認証トークンを生成
            token = AuthToken.objects.create(user)[1]

            return Response({
                "user": user_data,
                "token": token
            })

        except serializers.ValidationError as e:
            logger.error(f"Login failed for identifier {request.data.get('identifier', 'identifier not provided')}: {str(e)}")
            logger.error(f"Serializer errors: {serializer.errors}")

            error_msg = serializer.errors.get('non_field_errors', ['ログインに失敗しました。'])[0]
            return Response({
                "error": error_msg,
                "details": serializer.errors
            }, status=status.HTTP_400_BAD_REQUEST)

        except Exception as e:
            logger.error(f"Unexpected error during login for identifier {request.data.get('identifier', 'identifier not provided')}: {str(e)}")
            logger.error(f"Traceback: {traceback.format_exc()}")
      
            return Response({
                "error": "予期せぬエラーが発生しました。",
                "details": str(e)
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)



    def post_old(self, request, *args, **kwargs):
        logger.info(f"Login attempt for user: {request.data.get('email', 'email not provided')}")
        logger.debug(f"Request data: {request.data}")

        serializer = self.get_serializer(data=request.data)
        try:
            serializer.is_valid(raise_exception=True)
            user = serializer.validated_data
            logger.info(f"User {user.email} logged in successfully")

            return Response({
                "user": UserSerializer(user, context=self.get_serializer_context()).data,
                "token": AuthToken.objects.create(user)[1]
            })
        except serializers.ValidationError as e:
            logger.error(f"Login failed for user {request.data.get('email', 'email not provided')}: {str(e)}")
            logger.error(f"Serializer errors: {serializer.errors}")

            error_msg = serializer.errors.get('non_field_errors', ['ログインに失敗しました。'])[0]
            return Response({
                "error": error_msg,
                "details": serializer.errors
            }, status=status.HTTP_400_BAD_REQUEST)
        except Exception as e:
            logger.error(f"Unexpected error during login for user {request.data.get('email', 'email not provided')}: {str(e)}")
            logger.error(f"Traceback: {traceback.format_exc()}")
      
            return Response({
                "error": "予期せぬエラーが発生しました。",
                "details": str(e)
            }, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


class UserUpdateAPI(generics.UpdateAPIView):
    permission_classes = [permissions.IsAuthenticated]
    serializer_class = UserUpdateSerializer

    def get_object(self):
        return self.request.user

    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            instance._prefetched_objects_cache = {}

        return Response(serializer.data)

# User 情報取得
class UserAPI(generics.RetrieveAPIView):
    permission_classes = [permissions.IsAuthenticated,]
    serializer_class = UserSerializer

    def get_object(self):
        return self.request.user

@api_view(['POST',])
@permission_classes((IsAuthenticated,))
@csrf_exempt 
class GoalImageViewSet_old(APIView):
    permissions_classes = [permissions.IsAuthenticated,]
    # parser_classes = [MultiPartParser, JSONParser]

    def post(self, request, format=None):
        # print(request.data)
        serializer = GolaImageSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HttP_200_OK)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        return Response({"ok":"ok"})

class GoalImageViewSet(viewsets.ModelViewSet):
    queryset=GoalImages.objects.all()
    serializer_class=GolaImageSerializer
    # parser_classes = (MultiPartParser, JSONParser)

    def get_queryset(self):
        queryset = GoalImages.objects.all()
    #     dist = self.request.GET.get('dist')
    #     if dist != None :
    #         queryset = Incident.objects.filter(entity=dist, is_approved=True)
    #     else:
    #         queryset = Incident.objects.filter(is_approved=True)
        return queryset

class CheckinImageViewSet(viewsets.ModelViewSet):
    queryset=CheckinImages.objects.all()
    serializer_class=CheckinImageSerializer
    # parser_classes = (MultiPartParser, JSONParser)

    def get_queryset(self):
        queryset = CheckinImages.objects.all()
    #     dist = self.request.GET.get('dist')
    #     if dist != None :
    #         queryset = Incident.objects.filter(entity=dist, is_approved=True)
    #     else:
    #         queryset = Incident.objects.filter(is_approved=True)
        return queryset
    

class RetrieveUserView(generics.RetrieveAPIView):
    queryset = CustomUser.objects.all()
    serializer_class = UserSerializer
    permission_classes = [IsAuthenticated]

    def get_object(self):
        return self.request.user


def userDetials(request):
    user_id = request.GET.get('user_id')
    user = CustomUser.objects.get(id=user_id)
    rogUser = RogUser.objects.filter(user=user)
    serializer = RogUserSerializer(rogUser, many=True)
    return JsonResponse(serializer.data, safe=False)

@api_view(['GET'])
@permission_classes((IsAuthenticated, ))
def DeleteAccount(request):
    usr = request.user;
    #print("user is" + usr)
    if(usr):
        #usr.delete()
        usr.email = usr.email + "_res" + str(uuid.uuid4())
        usr.save();
        AuthToken.objects.filter(user=usr).delete()
        return Response({"result":"user deleted"})
    return Response({"result":"user not found"})

'''
    def send_verification_email(self, temp_user):
        subject = '仮登録の確認'
        message = f'以下のリンクをクリックして登録を完了してください：\n{settings.FRONTEND_URL}/verify/{temp_user.verification_code}'
        from_email = settings.DEFAULT_FROM_EMAIL
        recipient_list = [temp_user.email]
        
        try:
            send_mail(subject, message, from_email, recipient_list)
            logger.info(f"Verification email sent to {temp_user.email}")
        except Exception as e:
            logger.error(f"Failed to send verification email to {temp_user.email}. Error: {str(e)}")
'''


def UserActionViewset(request):
    user_id = request.GET.get('user_id')
    location_id = request.GET.get('location_id')
    location = Location2025.objects.get(location_id=location_id)
    user = CustomUser.objects.get(id=user_id)
    action = Useractions.objects.filter(location=location, user=user)
    serializer = UseractionsSerializer(action, many=True)
    return JsonResponse(serializer.data, safe=False)


def UserMakeActionViewset(request):
    user_id = request.GET.get('user_id')
    location_id = request.GET.get('location_id')
    wanttogo = True if request.GET.get('wanttogo') == "true" else False
    like = True if request.GET.get('like') == "true" else False
    checkin = True if request.GET.get('checkin') == "true" else False
    location = Location2025.objects.get(location_id=location_id)
    user = CustomUser.objects.get(id=user_id)
    #action = Useractions.objects.filter(location__id=location_id, user__id=user_id)
    rec = Useractions.objects.filter(user=user, location=location)
    if(rec):
        obj = rec.update(wanttogo=wanttogo, like=like, checkin=checkin)
    else:
        obj, created = Useractions.objects.update_or_create(user=user, location=location, wanttogo=wanttogo, like=like, checkin=checkin)
    serializer = UseractionsSerializer(obj, many=False)
    return JsonResponse(serializer.data, safe=False)


def UserDestinations(request):
        user_id = request.GET.get('user_id')
        user = CustomUser.objects.get(id=user_id)
        #action = Useractions.objects.filter(location__id=location_id, user__id=user_id)
        rec = Useractions.objects.filter(user=user, wanttogo=True).order_by('order')
        serializer = UserDestinationSerializer(rec, many=True)
        return JsonResponse(serializer.data, safe=False)


def UpdateOrder(request):
    dir = request.GET.get('dir')
    user_action_id = int(request.GET.get('user_action_id'))
    order = int(request.GET.get('order'))
    aorder = int(request.GET.get('order'))
    oorder = int(request.GET.get('order'))
    if(user_action_id):
        #updated = Useractions.objects.filter(order__gte=order).update(order = F('order')+1)
        #res = Useractions.objects.filter(id=user_action_id).update(order=order)
        index = 0
        if dir == "up":
            for id in Useractions.objects.all().order_by('order').values_list('id', flat=True):
                print(id)
                print("----",user_action_id)
                if index == order :
                    index += 1
                    print("index increated .....")
                if user_action_id == id:
                    Useractions.objects.filter(id=id).update(order=order)
                    print("updated .....")
                    continue
                    
                Useractions.objects.filter(id=id).update(order=index)
                index += 1
        else :
            for id in Useractions.objects.all().order_by('order').values_list('id', flat=True):
                print(id)
                print("----",user_action_id)
                if index == order :
                    index -= 1
                    print("index increated .....")
                if user_action_id == id:
                    Useractions.objects.filter(id=id).update(order=order)
                    print("updated .....")
                    continue
                    
                Useractions.objects.filter(id=id).update(order=index)
                index += 1
        # for id in Useractions.objects.values_list('order', flat=True):
        #     aorder -= 1
        #     Useractions.objects.filter(order__lt=id).update(order=aorder)
        # res = Useractions.objects.filter(id=user_action_id).update(order=oorder)
        
        return JsonResponse(1, safe=False)
    else:
        return JsonResponse({}, safe=False)


def DeleteDestination(request):
    dest_id = int(request.GET.get('dest_id'))
    print("###### dest   ")
    print(dest_id)
    if(dest_id != None):
        Useractions.objects.filter(id=dest_id).delete()
        return JsonResponse({"success":1}, safe=False)
    else:
        return JsonResponse({"success":0}, safe=False)


def CustomAreaLocations(request):
    
    cat = request.GET.get('cat')
    name = request.GET.get('name')
    is_rog = request.GET.get('rog')
    grp = request.GET.get('grp')

    if(cat != None):
        if is_rog:
            if grp:
                locs = Location2025.objects.filter(~Q(cp=0), event_name__isnull=False, category=cat, event_name=name, group__contains=grp)
            else:
                locs = Location2025.objects.filter(~Q(cp=0), event_name__isnull=False, category=cat, event_name=name)
        else:
            if grp:
                locs = Location2025.objects.filter(event_name__isnull=False, category=cat, event_name=name, group__contains=grp, location_name__contains='観光')
            else:
                locs = Location2025.objects.filter(event_name__isnull=False, category=cat, event_name=name, location_name__contains='観光')
    else:
        if is_rog:
            if grp:
                locs = Location2025.objects.filter(~Q(cp=0), event_name__isnull=False, event_name=name, group__contains=grp)
            else:
                locs = Location2025.objects.filter(~Q(cp=0), event_name__isnull=False, event_name=name)
        else:
            if grp:
                locs = Location2025.objects.filter(event_name__isnull=False, event_name=name, group__contains=grp, location_name__contains='観光')
            else:
                locs = Location2025.objects.filter(event_name__isnull=False, event_name=name, location_name__contains='観光')
    
    serializer = LocationSerializer(locs, many=True) 
    return JsonResponse(serializer.data, safe=False)


def CustomAreaNames(request):
    locs = Location2025.objects.filter(event_name__isnull=False).values('event_name').distinct()

    serializer = LocationEventNameSerializer(locs, many=True)
    return JsonResponse(serializer.data, safe=False)


class UserActivationView(APIView):

    def get(self, request, activation_token):
        try:
            temp_user = TempUser.objects.get(verification_code=activation_token)
            user = CustomUser.objects.create(
                email=temp_user.email,
                firstname=temp_user.firstname,
                lastname=temp_user.lastname,
                date_of_birth=temp_user.date_of_birth,
                female=temp_user.female,
                is_active=True
            )
            # Here you might want to add the user to the team they were invited to
            temp_user.delete()
            return Response({"message": "アカウントが正常にアクティベートされました。"}, status=status.HTTP_200_OK)
        except TempUser.DoesNotExist:
            return Response({"error": "無効なアクティベーショントークンです。"}, status=status.HTTP_400_BAD_REQUEST)

    '''
    def get(self, request, activation_token):
        try:
            user = CustomUser.objects.get(activation_token=activation_token, is_active=False)
            user.is_active = True
            user.activation_token = None
            user.save()
            return Response({"message": "アカウントが正常にアクティベートされました。"}, status=status.HTTP_200_OK)
        except CustomUser.DoesNotExist:
            return Response({"error": "無効なアクティベーショントークンです。"}, status=status.HTTP_400_BAD_REQUEST)
    '''

class ChangePasswordView(generics.UpdateAPIView):
    """
    An endpoint for changing password.
    """
    serializer_class = ChangePasswordSerializer
    model = CustomUser
    permission_classes = (IsAuthenticated,)

    def get_object(self, queryset=None):
        obj = self.request.user
        return obj

    def update(self, request, *args, **kwargs):
        self.object = self.get_object()
        serializer = self.get_serializer(data=request.data)

        if serializer.is_valid():
            # Check old password
            if not self.object.check_password(serializer.data.get("old_password")):
                return Response({"old_password": ["Wrong password."]}, status=status.HTTP_400_BAD_REQUEST)
            # set_password also hashes the password that the user will get
            self.object.set_password(serializer.data.get("new_password"))
            self.object.save()
            response = {
                'status': 'success',
                'code': status.HTTP_200_OK,
                'message': 'Password updated successfully',
                'data': []
            }

            return Response(response)

        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class TestActionViewSet(viewsets.ModelViewSet):
    serializer_class = TestSerialiser
    queryset = TestModel.objects.all()


def PrivacyView(request):
    return render(request, "rog/privacy.html")


class RegistrationView(APIView):
    @transaction.atomic
    def post(self, request):
        serializer = UserRegistrationSerializer(data=request.data)
        if serializer.is_valid():
            try:
                user = serializer.save()
                logger.info(f"New user registered: {user.email}")
                    
                # パスワードを取得
                password = serializer.validated_data.pop('password')
                # ユーザーを作成するが、まだ保存しない
                user = serializer.save(commit=False)
                # パスワードを明示的に設定
                user.set_password(password)

                # ユーザーを保存
                user.save()
                logger.info(f"New user registered: {user.email}")
                return Response({"message": "ユーザー登録が完了しました。"}, status=status.HTTP_201_CREATED)


            except Exception as e:
                logger.error(f"Error during user registration: {str(e)}")
                return Response({"error": "ユーザー登録中にエラーが発生しました。"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
        else:
            logger.warning(f"Invalid registration data: {serializer.errors}")
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)




# Akira

class NewEvent2ViewSet(viewsets.ModelViewSet):
    queryset = NewEvent2.objects.all()
    serializer_class = NewEvent2Serializer
    
    def get_permissions(self):
        """
        GETメソッドは認証不要、その他のメソッドは認証必要
        """
        if self.action in ['list', 'retrieve']:
            permission_classes = [permissions.AllowAny]
        else:
            permission_classes = [permissions.IsAuthenticated]
        return [permission() for permission in permission_classes]

class NewEvent2ListView(generics.ListAPIView):
    queryset = NewEvent2.objects.all()
    serializer_class = NewEvent2Serializer
    permission_classes = [permissions.IsAuthenticated]

class NewEventViewSet(viewsets.ModelViewSet):
    queryset = NewEvent.objects.all()
    serializer_class = NewEventSerializer
    permission_classes = [permissions.IsAuthenticated]

class NewEventListView(generics.ListAPIView):
    queryset = NewEvent.objects.all()
    serializer_class = NewEventSerializer
    permission_classes = [permissions.IsAuthenticated]




class TeamViewSet(viewsets.ModelViewSet):
    serializer_class = TeamSerializer
    permission_classes = [permissions.IsAuthenticated, IsTeamOwner]

    def get_queryset(self):
        return Team.objects.filter(owner=self.request.user)

    def perform_create(self, serializer):
        logger.info(f"Creating new team for user: {self.request.user.email}")
        team = serializer.save(owner=self.request.user)
        logger.info(f"Team created successfully: {team.id}")

    def update_related_entries(self, team):
        current_entries = Entry.objects.filter(team=team, is_active=True)
        for entry in current_entries:
            self.update_external_system(entry.zekken_number, team.owner.event_code, team.team_name)

    def update_external_system(self, zekken_number, event_code, team_name):
        api_url = f"{settings.FRONTEND_URL}/gifuroge/update_team_name"
        headers = {"Content-Type": "application/x-www-form-urlencoded"}
        data = {
            #"zekken_number": zekken_number,
            "new_team_name": team_name,
            #"event_code": event_code,
        }
        try:
            response = requests.post(api_url, headers=headers, data=data)
            response.raise_for_status()
            logger.info(f"External system updated for zekken_number: {zekken_number}")
            return True
        except requests.RequestException as e:
            logger.error(f"Failed to update external system. Error: {str(e)}")
            return False


        
#        with transaction.atomic():
#
#            category = serializer.validated_data['category']
#            category = NewCategory.objects.select_for_update().get(id=category.id)
#            zekken_number = category.category_number
#            category.category_number = F('category_number') + 1
#            category.save()
#            category.refresh_from_db()  # F() 式の結果を評価
#
#            team = serializer.save(owner=self.request.user, zekken_number=zekken_number)
#            logger.info(f"Team created successfully: {team.id}")
#
#            # 外部システムの更新
#            success = self.register_team(
#                team.owner,
#                team.zekken_number,
#                team.owner.event_code,
#                team.team_name,
#                team.category.category_name,
#                team.owner.password
#            )
#            if not success:
#                logger.error("Failed to register external system")
#                raise serializers.ValidationError("外部システムの更新に失敗しました。")
#            logger.info("External system register successfully")
#
#    def register_team(self, owner,zekken_number,event_code,team_name,category_name,password):
#        logger.info(f"register_team ==> zekken_number={zekken_number},event_code={event_code},team_name={team_name},category_name={category_name},password={password}")
#        api_url = f"{settings.FRONTEND_URL}/gifuroge/register_team"
#        user = owner
#
#        headers = {
#                "Content-Type": "application/x-www-form-urlencoded"
#                }
#        data = {
#            "zekken_number": zekken_number,
#            "event_code": event_code,
#            "team_name": team_name,
#            "class_name": category_name,
#            "password": password  # パスワードの扱いに注意が必要です
#        }
#
#        try:
#            response = requests.post(api_url,headers=headers,data=data)
#            response.raise_for_status()
#            logger.info(f"Team registered successfully for team {team_name}")
#            return True
#
#        except requests.RequestException as e:
#            logger.error(f"Failed to register team for entry {entry.id}. Error: {str(e)}")
#            # エラーが発生しても、エントリー自体は作成されています
#            # 必要に応じて、ここでエラーハンドリングを追加できます
#            return False
#
#    def destroy(self, request, *args, **kwargs):
#        team = self.get_object()
#        if team.members.exists():
#            return Response(
#                {
#                    "error": "チームにメンバーが残っているため削除できません。",
#                    "error_code": "TEAM_HAS_MEMBERS"
#                },
#                status=status.HTTP_400_BAD_REQUEST
#            )
#
#            #return Response({"error": "チームにメンバーが残っているため削除できません。"}, status=status.HTTP_400_BAD_REQUEST)
#        return super().destroy(request, *args, **kwargs)
#
#    def update_external_system(zekken_number, event_code, team_name, class_name, password):
#        api_url = f"{settings.FRONTEND_URL}/gifuroge/update_team_name"
#        headers = {
#                    "Content-Type": "application/x-www-form-urlencoded"
#                    }
#        data = {
#            "zekken_number": zekken_number,
#            "new_team_name": team_name,
#            "event_code": event_code,
#        }
#        try:
#            response = requests.post(api_url,headers=headers, data=data)
#            response.raise_for_status()
#    
#            return True
#        except requests.RequestException as e:
#            logger.error(f"Failed to update external system. Error: {str(e)}")
#            return False
#
#    def update(self, request, *args, **kwargs):
#        try:
#            partial = kwargs.pop('partial', False)
#            instance = self.get_object()
#            serializer = self.get_serializer(instance, data=request.data, partial=partial)
#            serializer.is_valid(raise_exception=True)
#            self.perform_update(serializer)
#
#            if getattr(instance, '_prefetched_objects_cache', None):
#                instance._prefetched_objects_cache = {}
#
#            return Response(serializer.data)
#        except Exception as e:
#            return Response({"error": "更新に失敗しました。競合が発生した可能性があります。"}, status=status.HTTP_409_CONFLICT)
#
#
##    def update(self, request, *args, **kwargs):
##        try:
##            return super().update(request, *args, **kwargs)
##        except Exception as e:
##            return Response({"error": "更新に失敗しました。競合が発生した可能性があります。"}, status=status.HTTP_409_CONFLICT)
#
#    def perform_update(self, serializer):
#        with transaction.atomic():
#            team = serializer.save()
#            
#            # 外部システムの更新
#            success = update_external_system(
#                team.zekken_number,
#                team.owner.event_code,
#                team.team_name,
#                team.category.category_name,
#                team.owner.password
#            )
#            if not success:
#                raise serializers.ValidationError("外部システムの更新に失敗しました。")
#            else:
#                print("岐阜ロゲシステム更新に成功")
#
#    @action(detail=True, methods=['post'])
#    def copy(self, request, pk=None):
#        original_team = self.get_object()
#        with transaction.atomic():
#            category = NewCategory.objects.select_for_update().get(id=original_team.category.id)
#            new_zekken_number = category.category_number
#            category.category_number = F('category_number') + 1
#            category.save()
#            category.refresh_from_db()  # F() 式の結果を評価
#
#            new_team = Team.objects.create(
#                zekken_number=new_zekken_number,
#                team_name=f"{original_team.team_name} (コピー)",
#                category=category,
#                owner=request.user
#            )
#            for member in original_team.members.all():
#                Member.objects.create(
#                    team=new_team,
#                    user=member.user
#                )
#        return Response(TeamSerializer(new_team).data, status=status.HTTP_201_CREATED)
#

class CategoryDetailView(generics.RetrieveAPIView):
    serializer_class = NewCategorySerializer
    
    def get_object(self):
        name = unquote(self.kwargs['name'])  # URLデコード
        return get_object_or_404(NewCategory, category_name=name)


class CategoryByNameView(generics.RetrieveAPIView):
    serializer_class = NewCategorySerializer
    
    def get_object(self):
        category_name = self.kwargs.get('name')
        return get_object_or_404(NewCategory, category_name=category_name)



class NewCategoryViewSet(viewsets.ModelViewSet):
    queryset = NewCategory.objects.all()
    serializer_class = NewCategorySerializer
    #permission_classes = [IsAuthenticated]

    def get_permissions(self):
        """
        GETメソッドは認証不要、その他のメソッドは認証必要
        """
        if self.action in ['list', 'retrieve']:
            permission_classes = [permissions.AllowAny]
        else:
            permission_classes = [permissions.IsAuthenticated]
        return [permission() for permission in permission_classes]


    @action(detail=True, methods=['POST'])
    def get_zekken_number(self, request, pk=None):
        try:
            with transaction.atomic():
                category = NewCategory.objects.select_for_update().get(pk=pk)
                category.category_number += 1
                category.save()
                serializer = self.get_serializer(category)
                return Response(serializer.data)
        except NewCategory.DoesNotExist:
            return Response({"error": "Category not found"}, status=status.HTTP_404_NOT_FOUND)
        except Exception as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

class NewCategoryListView(generics.ListAPIView):
    queryset = NewCategory.objects.all()
    serializer_class = NewCategorySerializer
    #permission_classes = [IsAuthenticated]

    def get_permissions(self):
        """
        GETメソッドは認証不要、その他のメソッドは認証必要
        """
        if self.request.method == 'GET':
            permission_classes = [permissions.AllowAny]
        else:
            permission_classes = [permissions.IsAuthenticated]
        return [permission() for permission in permission_classes]        


class CategoryViewSet(viewsets.ModelViewSet):
    queryset = Category.objects.all()
    serializer_class = CategorySerializer
    permission_classes = [permissions.IsAuthenticated]

class CategoryListView(generics.ListAPIView):
    queryset = Category.objects.all()
    serializer_class = CategorySerializer
    permission_classes = [permissions.IsAuthenticated]

'''
    def get(self, request):
        categories = Category.objects.all()
        data = []
        for category in categories:
            category_name = force_str(category.category_name)
            data.append({
                'category_name': category_name,
                # その他のフィールド
            })
        return Response(data)
'''

class EntryViewSet(viewsets.ModelViewSet):
    #queryset = Entry.objects.all()
    serializer_class = EntrySerializer
    permission_classes = [permissions.IsAuthenticated]

    #def perform_create(self, serializer):
    #    team = Team.objects.get(owner=self.request.user)
    #    serializer.save(team=team)

    def get_queryset(self):
        user = self.request.user
        # ユーザーが所有するチームのIDを取得
        owned_team_ids = Team.objects.filter(owner=user).values_list('id', flat=True)
        # ユーザーがメンバーとして所属するチームのIDを取得
        member_team_ids = Member.objects.filter(user=user).values_list('team_id', flat=True)
        # 両方のチームに関連するエントリーを取得
        return Entry.objects.filter(Q(team__id__in=owned_team_ids) | Q(team__id__in=member_team_ids), is_active=True)
        #return Entry.objects.filter(Q(team__id__in=owned_team_ids) | Q(team__id__in=member_team_ids))

    @transaction.atomic
    def perform_create(self, serializer):
        try:
            category = serializer.validated_data['category']
            category = NewCategory.objects.select_for_update().get(id=category.id)
            zekken_number = category.category_number
            category.category_number = F('category_number') + 1
            category.save()
            category.refresh_from_db()
    
            team = serializer.validated_data['team']
            event = serializer.validated_data['event']
            event_name = event.event_name  # イベント名を取得
    
            entry = serializer.save(owner=self.request.user, zekken_number=zekken_number)
    
            logger.info(f"team.owner = {team.owner}, event_name = {event_name}")
            logger.info(f"team = {team}")

            # 外部システムの更新
            success = self.register_team(
                entry.zekken_number,
                event_name,
                team.team_name,
                category.category_name,
                team.owner.password
            )
            if not success:
                logger.error("Failed to register external system")
                raise serializers.ValidationError("外部システムの更新に失敗しました。")
            logger.info("External system registered successfully")
        except Exception as e:
            logger.exception(f"Error creating Entry: {str(e)}")


    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # イベントの締め切り日時をチェック
        event = serializer.validated_data['event']
        if event.deadlineDateTime and timezone.now() > event.deadlineDateTime:
            return Response(
                {"error": "締め切りを過ぎているため、エントリーを作成できません。"},
                status=status.HTTP_400_BAD_REQUEST
            )

        try:
            self.perform_create(serializer)
            headers = self.get_success_headers(serializer.data)
            return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
        except DjangoValidationError as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)


    def register_team(self, zekken_number, event_code, team_name, category_name, password):
        api_url = f"{settings.FRONTEND_URL}/gifuroge/register_team"
        headers = {"Content-Type": "application/x-www-form-urlencoded"}
        data = {
            "zekken_number": zekken_number,
            "event_code": event_code,
            "team_name": team_name,
            "class_name": category_name,
            "password": password
        }
        logger.info(f"data= {data}");
        try:
            response = requests.post(api_url, headers=headers, data=data)
            response.raise_for_status()
            logger.info(f"Team registered successfully for zekken_number {zekken_number}")
            return True
        except requests.RequestException as e:
            logger.error(f"Failed to register team. Error: {str(e)}")
            return False

    def update(self, request, *args, **kwargs):
        logger.info(f"Update method called for Entry with ID: {kwargs.get('pk')}")
        logger.debug(f"Request data: {request.data}")

        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)

        try:
            serializer.is_valid(raise_exception=True)
            logger.debug(f"Serializer validated data: {serializer.validated_data}")
        except serializers.ValidationError as e:
            logger.error(f"Validation error: {e.detail}")
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

        try:
            self.perform_update(serializer)
            logger.info(f"Entry updated successfully: {serializer.data}")
        except Exception as e:
            logger.exception(f"Error updating Entry: {str(e)}")
            return Response({"error": "An error occurred while updating the entry."}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

        #if getattr(instance, '_prefetched_objects_cache', None):
        #    instance._prefetched_objects_cache = {}
    
        return Response(serializer.data)


#    def create(self, request, *args, **kwargs):
#        serializer = self.get_serializer(data=request.data)
#        try:
#            serializer.is_valid(raise_exception=True)
#            self.perform_create(serializer)
#            headers = self.get_success_headers(serializer.data)
#            return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
#        except DRFValidationError as e:
#            return Response({'error': self.format_errors(e.detail)}, status=status.HTTP_400_BAD_REQUEST)
#        # except IntegrityError:
#        #    return Response({'error': '既に登録済みです'}, status=status.HTTP_400_BAD_REQUEST)
#        except IntegrityError as e:
#            return Response({'error': f'データベースエラー: {str(e)}'}, status=status.HTTP_400_BAD_REQUEST)
#
#        except Exception as e:
#            return Response({'error': f"予期せぬエラーが発生しました: {str(e),'type': str(type(e))}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
#
#    def perform_create(self, serializer):
#        serializer.save(owner=self.request.user)
#
#
#    def update(self, request, *args, **kwargs):
#        logger.info(f"Update method called for Entry with ID: {kwargs.get('pk')}")
#        logger.debug(f"Request data: {request.data}")
#
#        partial = kwargs.pop('partial', False)
#        instance = self.get_object()
#        serializer = self.get_serializer(instance, data=request.data, partial=partial)
#
#        try:
#            serializer.is_valid(raise_exception=True)
#            logger.debug(f"Serializer validated data: {serializer.validated_data}")
#        except serializers.ValidationError as e:
#            logger.error(f"Validation error: {e.detail}")
#            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
#
#        try:
#            self.perform_update(serializer)
#            logger.info(f"Entry updated successfully: {serializer.data}")
#        except Exception as e:
#            logger.exception(f"Error updating Entry: {str(e)}")
#            return Response({"error": "An error occurred while updating the entry."}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
#
#
#        return Response(serializer.data)
#
#    def destroy(self, request, *args, **kwargs):
#        instance = self.get_object()
#        self.perform_destroy(instance)
#        return Response(status=status.HTTP_204_NO_CONTENT)
#
#    def get_error_message(self, exception):
#        if hasattr(exception, 'detail'):
#            if isinstance(exception.detail, dict):
#                return '. '.join([f"{key}: {', '.join(value)}" for key, value in exception.detail.items()])
#            elif isinstance(exception.detail, list):
#                return '. '.join(exception.detail)
#        return str(exception)
#
#    def format_errors(self, errors):
#        if isinstance(errors, list):
#            return '. '.join(errors)
#        elif isinstance(errors, dict):
#            return '. '.join([f"{key}: {value}" if isinstance(value, str) else f"{key}: {', '.join(value)}" for key, value in errors.items()])
#        else:
#            return str(errors)

class TeamEntriesView(generics.ListAPIView):
    serializer_class = EntrySerializer
    permission_classes = [IsAuthenticated, IsTeamOwnerOrMember]

    def get_queryset(self):
        team_id = self.kwargs['team_id']
        team = Team.objects.get(id=team_id)
        return Entry.objects.filter(team=team)

    def get_serializer_context(self):
        context = super().get_serializer_context()
        context['team_id'] = self.kwargs['team_id']
        return context


class MemberViewSet(viewsets.ModelViewSet):
    serializer_class = MemberSerializer
    permission_classes = [permissions.IsAuthenticated,IsTeamOwner]

    def get_serializer_class(self):
        if self.action == 'create':
            return MemberCreationSerializer
        return MemberSerializer

    def get_queryset(self):
        team_id = self.kwargs['team_id']
        return Member.objects.filter(team_id=team_id)

    @action(detail=False, methods=['DELETE'])
    def destroy_all(self, request, team_id=None):
        team = Team.objects.get(id=team_id)
        
        # チームのオーナーかどうかを確認
        if team.owner != request.user:
            return Response({"error": "チームのオーナーのみがメンバーを一括削除できます。"}, status=status.HTTP_403_FORBIDDEN)

        # 確認パラメータをチェック
        confirm = request.query_params.get('confirm', '').lower()
        if confirm != 'true':
            return Response({"error": "確認パラメータが必要です。'?confirm=true'を追加してください。"}, status=status.HTTP_400_BAD_REQUEST)

        try:
            with transaction.atomic():
                # オーナー以外のすべてのメンバーを削除
                #deleted_count = Member.objects.filter(team=team).exclude(user=team.owner).delete()[0]
                deleted_count = Member.objects.filter(team=team).delete()[0]

            return Response({
                "message": f"{deleted_count}人のメンバーが削除されました。",
                "deleted_count": deleted_count
            }, status=status.HTTP_200_OK)

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

    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        team = instance.team
        
        # チームのオーナーかどうかを確認
        if team.owner != request.user:
            return Response({"error": "チームのオーナーのみがメンバーを削除できます。"}, status=status.HTTP_403_FORBIDDEN)
        
        # オーナー自身は削除できないようにする
        #if instance.user == team.owner:
        #    return Response({"error": "チームのオーナーは削除できません。"}, status=status.HTTP_400_BAD_REQUEST)

        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)

    def create(self, request, *args, **kwargs):
        logger.info(f"Attempting to create new member for team: {self.kwargs['team_id']}")
        logger.debug(f"Received data: {request.data}")
        
        team = Team.objects.get(id=self.kwargs['team_id'])
        #serializer = self.get_serializer(data=request.data)
        serializer = self.get_serializer(data=request.data, context={'team': team})

        try:
            serializer.is_valid(raise_exception=True)
        except serializers.ValidationError as e:
            logger.error(f"Validation error: {e.detail}")
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

        email = serializer.validated_data.get('email', '')
        logger.info(f"Processing member with email: {email}")

        user_data = {
            'firstname': serializer.validated_data.get('firstname', ''),
            'lastname': serializer.validated_data.get('lastname', ''),
            'date_of_birth': serializer.validated_data.get('date_of_birth'),
            'female': serializer.validated_data.get('female', False),
        }

        # 自分自身を登録する場合
        if request.user.email == email:
            member, created = Member.objects.get_or_create(user=request.user, team=team)
            if created:
                return Response(MemberSerializer(member).data, status=status.HTTP_201_CREATED)
            else:
                return Response({"message": "You are already a member of this team."}, status=status.HTTP_200_OK)

        if not email or email.startswith('dummy_'):
            logger.info("Processing as direct registration")
            # 直接登録
            try:
                if email:
                    user, created = CustomUser.objects.get_or_create(
                        email=email,
                        defaults={**user_data, 'is_active':True}
                    )
                else:
                    # emailが空の場合、一意のダミーメールアドレスを生成
                    dummy_email = f"dummy_{uuid.uuid4()}@example.com"
                    user = CustomUser.objects.create(email=dummy_email, **user_data)

                member = Member.objects.create(user=user, team=team)
                logger.info(f"Member created successfully: {member.id}")
                return Response(MemberSerializer(member).data, status=status.HTTP_201_CREATED)
            except Exception as e:
                logger.error(f"Error creating member: {str(e)}")
                return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)

            #member = serializer.save(team=team)
            #return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:
            logger.info("Processing as temporary registration")

            try:
                # 仮登録
                existing_user = CustomUser.objects.filter(email=email).first()
                if existing_user:
                    logger.info(f"Existing user found: {existing_user.id}")
    
                    # 既存ユーザーの場合、チーム招待メールを送信
                    send_team_join_email(request,request.user,existing_user, team)
                    return Response({"message": "Invitation for your team sent to existing user."}, status=status.HTTP_200_OK)
                else:
                    logger.info("Inviting new temporary user")
    
                    try:
                        print("新規ユーザー")
                        #temp_user = TempUser.objects.create(
                        #    email=email,
                        #    **user_data,
                        #    verification_code=str(uuid.uuid4())
                        #)
                        send_invitation_email(request.user,request,email, team)  #仮登録済みでも確認メールを送る。
                        logger.info(f"Invitation email sent to: {email}")
                        return Response({"message": "Invitation email sent to the user."}, status=status.HTTP_201_CREATED)
                    except Exception as e:
                        logger.info(f"Error on Invitation email sent to: {email} : {e}")
                        return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
            except Exception as e:
                logger.error(f"Error in create method: {str(e)}")
                return Response({"error": "An unexpected error occurred."}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)


    '''
    def create(self, request, *args, **kwargs):
        team = Team.objects.get(id=self.kwargs['team_id'])
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        try:
            self.perform_create(serializer)
        except DRFValidationError as e:
            return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
        #except ValidationError as e:
        #    return Response({"error": str(e)}, status=status.HTTP_400_BAD_REQUEST)
        except IntegrityError:
            return Response({"error": "このユーザーは既にチームのメンバーです。"}, status=status.HTTP_400_BAD_REQUEST)

        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
    '''

    '''
    def send_activation_email(self, temp_user, team):
        # アクティベーションメール送信のロジックをここに実装
        # 注意: 'user-activation' URLパターンが存在しない場合は、適切なURLを生成する方法を検討する必要があります

        activation_url = "dummy url"  #f"{settings.FRONTEND_URL}/activate/{temp_user.verification_code}"

        # ここでemailを送信するロジックを実装
        print(f"Activation email would be sent to {temp_user.email} with URL: {activation_url}")

    def send_invitation_email(self, user, team):
        # チーム招待メール送信のロジック
        #invitation_url = self.request.build_absolute_uri(
        #    reverse('team-invite', kwargs={'team_id': team.id, 'user_id': user.id})
        #)
        #subject = f"Invitation to join {team.name}"
        #message = f"You have been invited to join the team {team.name}. Click here to accept: {invitation_url}"
        #from_email = settings.DEFAULT_FROM_EMAIL
        #recipient_list = [user.email]
        
        #send_mail(subject, message, from_email, recipient_list)

        # チーム招待メール送信のロジックをここに実装
        # 注意: 'team-invitation' URLパターンが存在しない場合は、適切なURLを生成する方法を検討する必要があります
        # 例えば、フロントエンドのURLを直接指定する方法など
        invitation_url = "dummy url"  #f"{settings.FRONTEND_URL}/team/{team.id}/invite"
        # ここでemailを送信するロジックを実装
        print(f"Invitation email would be sent to {user.email} with URL: {invitation_url}")
    '''



    def perform_create(self, serializer):
        team = Team.objects.get(id=self.kwargs['team_id'])
        serializer.save(team=team)

    @transaction.atomic
    def update(self, request, *args, **kwargs):
        #partial = kwargs.pop('partial', False)
        instance = self.get_object()
        user = instance.user
        logger.debug(f"Updating user: {user.email}")
        logger.debug(f"Received data: {request.data}")

        if user.email.startswith('dummy_'):
            logger.debug("User has dummy email, proceeding with update")

            # 直接データを更新
            user.firstname = request.data.get('firstname', user.firstname)
            user.lastname = request.data.get('lastname', user.lastname)
 
            # 日付の処理
            date_of_birth = request.data.get('date_of_birth')
            if date_of_birth:
                try:
                    date_of_birth = date_of_birth.translate(str.maketrans("０１２３４５６７８９", "0123456789"))
                    parsed_date = parse_date(date_of_birth)
                    if parsed_date:
                        user.date_of_birth = parsed_date
                    else:
                        raise ValueError("Invalid date format")
                except ValueError:
                    logger.error(f"Invalid date format: {date_of_birth}")
                    return Response({"error": "Invalid date format. Use YYYY-MM-DD."}, status=status.HTTP_400_BAD_REQUEST)

            user.female = request.data.get('female', user.female)
            
            user.save()
            logger.debug(f"User updated: firstname={user.firstname}, lastname={user.lastname}, date_of_birth={user.date_of_birth}, female={user.female}")
            
            # Memberインスタンスも更新
            serializer = self.get_serializer(instance, data=request.data, partial=True)
            serializer.is_valid(raise_exception=True)
            self.perform_update(serializer)
            
            # 更新後のデータを取得
            updated_instance = self.get_object()
            updated_serializer = self.get_serializer(updated_instance)
            
            logger.debug(f"Updated user data: {updated_serializer.data}")
            return Response(updated_serializer.data)
        else:
            logger.debug("User does not have dummy email, update not allowed")
            return Response({"error": "このユーザーの情報は更新できません。"}, status=status.HTTP_403_FORBIDDEN)


    def perform_update(self, serializer):
        serializer.save()
        logger.debug("perform_update called")


    def get_object(self):
        queryset = self.get_queryset()
        member_id = self.kwargs['pk']
        obj = get_object_or_404(queryset, id=member_id)
        self.check_object_permissions(self.request, obj)
        return obj

class ActivateMemberView(APIView):
    def get(self, request, user_id, team_id):
        user = get_object_or_404(CustomUser, id=user_id)
        team = get_object_or_404(Team, id=team_id)

        # メンバーオブジェクトを取得または作成
        member, created = Member.objects.get_or_create(user=user, team=team)

        if member.lastname == "dummy":
            # userデータでmemberデータを更新
            member.lastname = user.lastname
            member.firstname = user.firstname
            member.date_of_birth = user.date_of_birth
            member.female = user.female
            member.save()

            message = f"{team.team_name}に正常に参加し、メンバー情報が更新されました。アプリのチーム管理で確認できます。"
        else:
            message = f"{team.team_name}に正常に参加しました。"

        return render(request, 'activation_success.html', {'message': message})


class ActivateNewMemberView(APIView):
    def get(self, request, verification_code, team_id):
        try:
            team = Team.objects.get(id=team_id)
            member, created = Member.objects.get_or_create(team=team, user__email=email)
            if created:
                return Response({'message': 'Member activated successfully'}, status=status.HTTP_201_CREATED)
            else:
                return Response({'message': 'Member already exists'}, status=status.HTTP_200_OK)
        except Team.DoesNotExist:
            return Response({'message': 'Invalid team'}, status=status.HTTP_400_BAD_REQUEST)

'''
        temp_user = get_object_or_404(TempUser, verification_code=verification_code)
        team = get_object_or_404(Team, id=team_id)
        
        user = CustomUser.objects.create_user(
            email=temp_user.email,
            password=temp_user.password,  # Ensure this is securely handled
            **{k: v for k, v in temp_user.__dict__.items() if k in ['firstname', 'lastname', 'date_of_birth', 'female']}
        )
        
        Member.objects.create(user=user, team=team)
        temp_user.delete()
        
        message = f"アカウントが作成され、{team.team_name}に参加しました。アプリのチーム管理で確認できます。"
        return render(request, 'activation_success.html', {'message': message})
'''


class OwnerEntriesView(generics.ListAPIView):
    serializer_class = EntrySerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        user = self.request.user
        return Entry.objects.filter(owner=user)

class OwnerTeamsView(generics.ListAPIView):
    serializer_class = TeamSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        user = self.request.user
        return Team.objects.filter(owner=user)

class OwnerMembersView(generics.ListAPIView):
    serializer_class = MemberSerializer
    permission_classes = [permissions.IsAuthenticated]

    def get_queryset(self):
        user = self.request.user
        return Member.objects.filter(team__owner=user)

class MemberAddView(APIView):
    def post(self, request, team_id):
        logger.info(f"Received request to add member to team {team_id}")
        logger.debug(f"Request data: {request.data}")

        team = get_object_or_404(Team, id=team_id)
        logger.info(f"Found team: {team}")

        serializer = MemberSerializer(data=request.data)
        if serializer.is_valid():
            logger.info("Serializer is valid")
            try:
                member = serializer.save(team=team)
                logger.info(f"Member added successfully: {member}")
                return Response(serializer.data, status=status.HTTP_201_CREATED)
            except Exception as e:
                logger.error(f"Error saving member: {str(e)}")
                return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
        else:
            logger.error(f"Serializer errors: {serializer.errors}")
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


class TempUserViewSet(viewsets.ModelViewSet):
    queryset = TempUser.objects.all()
    serializer_class = TempUserSerializer
    permission_classes = [IsAuthenticated]

# CustomUserViewSetの修正
class CustomUserViewSet(viewsets.ModelViewSet):
    queryset = CustomUser.objects.all()
    serializer_class = CustomUserSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        user = self.request.user
        if user.is_staff:
            return CustomUser.objects.all()
        return CustomUser.objects.filter(id=user.id)


class TeamMembersView(generics.ListAPIView):
    serializer_class = MemberSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        team_id = self.kwargs['team_id']
        return Member.objects.filter(team_id=team_id)

# ユーザーのエントリーを取得するビュー
class UserEntriesView(generics.ListAPIView):
    serializer_class = EntrySerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        user = self.request.user
        return Entry.objects.filter(team__owner=user)

# イベントのカテゴリーを取得するビュー
class EventCategoriesView(generics.ListAPIView):
    serializer_class = NewCategorySerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        event_id = self.kwargs['event_id']
        return NewCategory.objects.filter(entry__event_id=event_id).distinct()

# ユーザー仮登録
class RegisterView(APIView):
    def post(self, request):
        serializer = TempUserSerializer(data=request.data)
        if serializer.is_valid():
            temp_user = serializer.save()
            verification_code = uuid.uuid4()
            temp_user.verification_code = verification_code
            temp_user.save()

            verification_url = request.build_absolute_uri(
                    reverse('rog:verify-email', kwargs={'verification_code': verification_code})
            )
            send_verification_email(temp_user,verifiction_url)
            #send_mail(
            #    'Verify your email',
            #    f'Click the link to verify your email: {verification_url}',
            #    settings.DEFAULT_FROM_EMAIL,
            #    [temp_user.email],
            #    fail_silently=False,
            #)
            return Response({'message': 'Verification email sent'}, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class ResendInvitationEmailView(APIView):
    def post(self, request):
        email = request.data.get('email')
        if not email:
            return Response({"error": "メールアドレスを指定してください。"}, status=status.HTTP_400_BAD_REQUEST)

        try:
            temp_user = TempUser.objects.get(email=email)
            verification_url = request.build_absolute_uri(
                    reverse('rog:verify-email', kwargs={'verification_code': temp_user.verification_code})
            )
            send_verification_email(temp_user, verification_url)
            logger.info(f"招待メールを再送信しました。Email: {email}")
            return Response({"message": "招待メールを再送信しました。"}, status=status.HTTP_200_OK)
        except TempUser.DoesNotExist:
            logger.warning(f"仮登録されていないメールアドレスに対して招待メールの再送信が試みられました。Email: {email}")
            return Response({"error": "指定されたメールアドレスは仮登録されていません。"}, status=status.HTTP_404_NOT_FOUND)




class TempUserRegistrationView(APIView):
    def post(self, request):
        email = request.data.get('email')
        
        # レート制限: 同じIPアドレスからの重複リクエストを防ぐ
        client_ip = self.get_client_ip(request)
        cache_key_ip = f"register_rate_limit:{client_ip}"
        cache_key_email = f"register_email_limit:{email}"
        
        # 同じIPからの連続リクエストを制限（1分間に最大3回）
        ip_requests = cache.get(cache_key_ip, 0)
        if ip_requests >= 3:
            logger.warning(f"レート制限: IP {client_ip} からの過剰なリクエスト")
            return Response({
                "error": "リクエストが多すぎます。1分後に再試行してください。"
            }, status=status.HTTP_429_TOO_MANY_REQUESTS)
        
        # 同じメールアドレスへの連続リクエストを制限（30秒間に1回）
        email_request_time = cache.get(cache_key_email)
        current_time = time.time()
        if email_request_time and (current_time - email_request_time) < 30:
            logger.warning(f"メール送信制限: {email} への重複リクエスト")
            return Response({
                "error": "同じメールアドレスへの再送信は30秒後に可能です。"
            }, status=status.HTTP_429_TOO_MANY_REQUESTS)

        # 本登録済みのユーザーチェック
        if CustomUser.objects.filter(email=email).exists():
            logger.warning(f"既に本登録されているメールアドレスでの仮登録が試みられました。Email: {email}")
            return Response({"error": "このメールアドレスは既に本登録されています。"}, status=status.HTTP_400_BAD_REQUEST)

       # 仮登録済みのユーザーチェック
        try:
            temp_user = TempUser.objects.get(email=email)
            verification_url = request.build_absolute_uri(
                    reverse('rog:verify-email', kwargs={'verification_code': temp_user.verification_code})
            )
            send_verification_email(temp_user, verification_url)
            
            # キャッシュを更新
            cache.set(cache_key_ip, ip_requests + 1, 60)  # 1分間
            cache.set(cache_key_email, current_time, 30)   # 30秒間
            
            logger.info(f"既に仮登録されているユーザーに招待メールを再送信しました。Email: {email}")
            return Response({"message": "既に仮登録は行われていますが、招待メールを再送信しました。"}, status=status.HTTP_200_OK)
        except TempUser.DoesNotExist:
            # 新規仮登録
            serializer = TempUserRegistrationSerializer(data=request.data)
            if serializer.is_valid():
                # シリアライザのvalidated_dataからパスワードを取得
                password = serializer.validated_data.get('password')
                # パスワードをハッシュ化
                hashed_password = make_password(password)
                # ハッシュ化されたパスワードでTempUserを作成
                temp_user = serializer.save(password=hashed_password)

                verification_code = uuid.uuid4()
                temp_user.verification_code = verification_code
                temp_user.save()

                verification_url = request.build_absolute_uri(
                        reverse('rog:verify-email', kwargs={'verification_code': verification_code})
                )
                send_verification_email(temp_user, verification_url)
                
                # キャッシュを更新
                cache.set(cache_key_ip, ip_requests + 1, 60)  # 1分間
                cache.set(cache_key_email, current_time, 30)   # 30秒間
                
                logger.info(f"新規ユーザーを仮登録し、招待メールを送信しました。Email: {email}")
                return Response({"message": "仮登録が完了しました。招待メールを送信しました。"}, status=status.HTTP_201_CREATED)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def get_client_ip(self, request):
        """クライアントIPアドレスを取得"""
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')
        return ip



        #serializer = TempUserRegistrationSerializer(data=request.data)
        #if serializer.is_valid():
        #    temp_user = serializer.save()
        #    verification_code = uuid.uuid4()
        #    temp_user.verification_code = verification_code
        #    temp_user.save()
        #    verification_url = request.build_absolute_uri(
        #        reverse('verify-email', kwargs={'verification_code': verification_code})
        #    )
        #    send_verification_email(temp_user,verification_url) #招待メールを送る。
        #    return Response({"message": "仮登録が完了しました。招待メールを送信しました。"}, status=status.HTTP_201_CREATED)
        #return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

# アクティベーション
class VerifyEmailView(APIView):
    def get(self, request, verification_code):
        try:
            temp_user = TempUser.objects.get(verification_code=verification_code)
            if temp_user.is_valid():
                user_data = {
                    'email': temp_user.email,
                    'is_rogaining':True,    # ここでis_rogainingをTrueに設定
                    'password':temp_user.password,
                    'is_rogaining': temp_user.is_rogaining,
                    'zekken_number': temp_user.zekken_number,
                    'event_code': temp_user.event_code,
                    'team_name': temp_user.team_name,
                    'group': temp_user.group,
                    'firstname': temp_user.firstname,
                    'lastname': temp_user.lastname,
                    'date_of_birth': temp_user.date_of_birth,
                    'female': temp_user.female,
                }

                # パスワードを安全にハッシュ化
                #hashed_password = make_password(temp_user.password)

                try:
                    # CustomUserを作成
                    user = CustomUser.objects.create(**user_data)

                    #user = CustomUser.objects.create_user(
                    #    email=user_data['email'],
                    #    password=temp_user.password,     # ハッシュ化されたパスワードを直接使用
                    #    **{k: v for k, v in user_data.items() if k != 'email'}
                    #)
                except ValidationError as e:
                    # パスワードのバリデーションエラーなどの処理
                    return render(request, 'verification_error.html', {'message': str(e), 'title': 'エラー'})

                # チームへの追加処理（もし必要なら）
                if hasattr(temp_user, 'team_id'):
                    team = Team.objects.get(id=temp_user.team_id)
                    Member.objects.create(user=user, team=team)
                    message = f"メールアドレスが確認され、アカウントが作成されました。{team.team_name}のメンバーとして登録されています。アプリでログインして、必要な情報を入力してください。"
                else:
                    message = "メールアドレスが確認され、アカウントが作成されました。アプリでログインして、必要な情報を入力してください。"

                temp_user.delete()
                return render(request, 'verification_success.html', {'message': message,'title':"認証成功"})
            else:
                message = "確認リンクの有効期限が切れています。アプリから認証コードの再送信をしてください。"
                return render(request, 'verification_success.html', {'message': message,'title':'有効期限切れ'})

        except TempUser.DoesNotExist:
            message = "無効な確認コードです。"
            return render(request, 'verification_success.html', {'message': message,'title':'無効な確認コードです'})


class MemberUserDetailView(generics.RetrieveAPIView):
    serializer_class = MemberWithUserSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        return Member.objects.select_related('user', 'team')

class TeamMembersWithUserView(generics.ListAPIView):
    serializer_class = MemberWithUserSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        team_id = self.kwargs['team_id']
        return Member.objects.filter(team_id=team_id).select_related('user', 'team')


class PasswordResetRequestView(APIView):
    def post(self, request):
        serializer = PasswordResetRequestSerializer(data=request.data)
        if serializer.is_valid():
            email = serializer.validated_data['email']
            user = CustomUser.objects.filter(email=email).first()
            if user:
                token = default_token_generator.make_token(user)
                uid = urlsafe_base64_encode(force_bytes(user.pk))
                reset_link = f"{settings.FRONTEND_URL}/api/reset-password/{uid}/{token}/"
                send_reset_password_email(email,reset_link)

                return Response({"message": "Password reset email sent"}, status=status.HTTP_200_OK)
            return Response({"message": "User not found"}, status=status.HTTP_404_NOT_FOUND)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

class PasswordResetConfirmView(APIView):
    def get(self, request, uidb64, token):
        try:
            uid = force_str(urlsafe_base64_decode(uidb64))
            user = CustomUser.objects.get(pk=uid)
        except (TypeError, ValueError, OverflowError, CustomUser.DoesNotExist):
            user = None

        if user is not None and default_token_generator.check_token(user, token):
            return render(request, 'password_reset.html', {'uid': uidb64, 'token': token})
        else:
            return render(request, 'password_reset_invalid.html')

        if user is not None and default_token_generator.check_token(user, token):
            return Response({"message": "Token is valid"}, status=status.HTTP_200_OK)
        return Response({"message": "Invalid reset link"}, status=status.HTTP_400_BAD_REQUEST)


    def post(self, request, uidb64, token):
        try:
            uid = force_str(urlsafe_base64_decode(uidb64))
            user = CustomUser.objects.get(pk=uid)
        except (TypeError, ValueError, OverflowError, CustomUser.DoesNotExist):
            return Response({"error": "Invalid reset link"}, status=status.HTTP_400_BAD_REQUEST)

        if default_token_generator.check_token(user, token):
            serializer = PasswordResetConfirmSerializer(data=request.data)
            if serializer.is_valid():
                user.set_password(serializer.validated_data['new_password'])
                user.save()
                return Response({"message": "Password has been reset successfully"}, status=status.HTTP_200_OK)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
        return Response({"error": "Invalid reset link"}, status=status.HTTP_400_BAD_REQUEST)


class UserLastGoalTimeView(APIView):
    def get(self, request, user_id):
        try:
            user = CustomUser.objects.get(id=user_id)
            last_goal = GoalImages.objects.filter(
                user=user
            ).aggregate(last_goal_time=Max('goaltime'))
            
            if last_goal['last_goal_time']:
                return Response({
                    'user_email': user.email,
                    'last_goal_time': last_goal['last_goal_time']
                })
            else:
                return Response({
                    'message': 'No goal records found for this user'
                }, status=status.HTTP_404_NOT_FOUND)
        except CustomUser.DoesNotExist:
            return Response({
                'message': 'User not found'
            }, status=status.HTTP_404_NOT_FOUND)


# ----- for Supervisor -----

@api_view(['GET'])
def debug_urls(request):
    """デバッグ用：利用可能なURLパターンを表示"""
    resolver = get_resolver()
    urls = []
    for pattern in resolver.url_patterns:
        try:
            urls.append(str(pattern.pattern))
        except:
            urls.append(str(pattern))
    return JsonResponse({'urls': urls})

@api_view(['GET'])
def get_events(request):
    logger.debug(f"get_events was called. Path: {request.path}")
    try:
        events = NewEvent2.objects.filter(public=True)
        logger.debug(f"Found {events.count()} events")  # デバッグ用ログ
        return Response([{
            'code': event.event_name,
            'name': event.event_name,
            'start_datetime': event.start_datetime,
            'end_datetime': event.end_datetime,
        } for event in events])
        logger.debug(f"Returning data: {data}")  # デバッグ用ログ
        return JsonResponse(data, safe=False)
    except Exception as e:
        print(f"Error in get_events: {str(e)}")  # デバッグ用
        return Response(
            {"error": "Failed to retrieve events"}, 
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )
@api_view(['GET'])
def get_zekken_numbers(request, event_code):
    # 通過審査画面用: GpsCheckinテーブルから過去の移行データと新規Entryテーブルの両方をサポート
    
    try:
        print(f"=== get_zekken_numbers called with event_code: {event_code} ===")
        
        # event_codeからNewEvent2のIDを取得
        try:
            event_obj = NewEvent2.objects.get(event_code=event_code)
            event_id = event_obj.id
            print(f"Found event: {event_obj.event_name} (ID: {event_id})")
        except NewEvent2.DoesNotExist:
            print(f"No event found with event_code: {event_code}, trying legacy data only")
            event_id = None
        
        entry_list = []
        # 新規のEntryテーブルから取得（event_idが見つかった場合のみ）
        if event_id:
            entries = Entry.objects.filter(
                event_id=event_id,
                zekken_number__gt=0
            ).values_list('zekken_label', flat=True).order_by('zekken_number')
        
        if event_id:
            entries = Entry.objects.filter(
                event_id=event_id,
                zekken_number__gt=0
            ).values_list('zekken_label', flat=True).order_by('zekken_number')
            
            entry_list = list(entries)
            print(f"Entry table found {len(entry_list)} records: {entry_list[:10]}")
        
        # GpsCheckinテーブルからも検索（過去の移行データ）
        gps_checkins = GpsCheckin.objects.filter(
            event_code=event_code,
            zekken__gt=''
        ).values_list('zekken', flat=True).distinct().order_by('zekken')
        
        gps_list = list(gps_checkins)
        print(f"GpsCheckin table found {len(gps_list)} records: {gps_list[:10]}")
        
        # 両方の結果をマージして重複を除去
        all_zekken_numbers = entry_list + gps_list
        unique_zekken_numbers = sorted(set(all_zekken_numbers))
        
        print(f"Final result: {unique_zekken_numbers[:10]}")
        
        return Response(unique_zekken_numbers)
    
    except Exception as e:
        print(f"Error in get_zekken_numbers: {str(e)}")
        import traceback
        traceback.print_exc()
        return Response({"error": str(e)}, status=500)

@api_view(['GET'])
def get_team_info(request, zekken_number):
    # zekken_numberまたはzekken_labelで検索を試行
    entry = None
    try:
        # まずzekken_numberで検索（数値の場合）
        if zekken_number.isdigit():
            entry = Entry.objects.select_related('team','event').get(zekken_number=int(zekken_number))
        else:
            # 数値でない場合はzekken_labelで検索
            entry = Entry.objects.select_related('team','event').get(zekken_label=zekken_number)
    except Entry.DoesNotExist:
        # zekken_numberで見つからない場合はzekken_labelで再試行
        try:
            entry = Entry.objects.select_related('team','event').get(zekken_label=zekken_number)
        except Entry.DoesNotExist:
            # 最後にzekken_numberとして再試行（数値以外でもエラーハンドリング）
            try:
                entry = Entry.objects.select_related('team','event').get(zekken_number=zekken_number)
            except (Entry.DoesNotExist, ValueError):
                return Response({'error': 'Entry not found'}, status=404)
    
    if not entry:
        return Response({'error': 'Entry not found'}, status=404)
    members = Member.objects.filter(team=entry.team)

    start_datetime = entry.event.start_datetime #イベントの規定スタート時刻
    if entry.event.self_rogaining:
        #get_checkinsの中で、
        #  start_datetime = -1(ロゲ開始)のcreate_at
        logger.debug(f"self.rogaining={entry.event.self_rogaining} => start_datetime = -1(ロゲ開始)のcreate_at")   

    # チームの最初のゴール時間を取得
    logger.info(f"Searching for GoalImages with team_name='{entry.team.team_name}', event_code='{entry.event.event_name}'")
    
    goal_record = GoalImages.objects.filter(
        team_name=entry.team.team_name,
        event_code=entry.event.event_name
    ).order_by('goaltime').first()

    # Nullチェックを追加してからログ出力
    goalimage_url = None
    goaltime = None

    if goal_record:
        logger.info(f"Found goal record: id={goal_record.id}, goaltime={goal_record.goaltime}")
        try:
            goaltime = goal_record.goaltime
            if goal_record.goalimage:
                # S3対応のget_image_url関数を使用
                goalimage_url = get_image_url(goal_record.goalimage)
                if goalimage_url:
                    logger.info(f"get_team_info record.goalimage_url={goalimage_url}")
                else:
                    logger.warning(f"Failed to generate URL for goal image")
            else:
                logger.info(f"Goal record exists but no image found: goalimage={goal_record.goalimage}")
                
        except ValueError as e:
            logger.warning(f"Error accessing goal image: {str(e)}")
    else:
        logger.info(f"No goal record found for team_name='{entry.team.team_name}', event_code='{entry.event.event_name}'")


    return Response({
        'team_name': entry.team.team_name,
        'members': ', '.join([f"{m.lastname} {m.firstname}" for m in members]),
        'event_code': entry.event.event_name,
        'start_datetime': entry.event.start_datetime,
        'end_datetime': goaltime, #goal_record.goaltime if goal_record else None,
        'goal_photo': goalimage_url, #goal_record.goalimage if goal_record else None,
        'duration': entry.category.duration.total_seconds()   
    })

    def create(self, request, *args, **kwargs):
        logger.info(f"Attempting to create new member for team: {self.kwargs['team_id']}")
        logger.debug(f"Received data: {request.data}")

        team = Team.objects.get(id=self.kwargs['team_id'])

def get_image_url(image_path):
    """画像URLを生成する補助関数 - S3とローカルメディアの両方に対応"""
    if not image_path:
        return None
    
    image_path_str = str(image_path)
    
    # シミュレーション画像の場合はローカルメディアパスを返す
    if image_path_str in ['simulation_image.jpg', '/media/simulation_image.jpg']:
        return f"{settings.MEDIA_URL}simulation_image.jpg"
    
    # ローカルメディアのフルパス（/media/で始まる）の場合は、S3パスを抽出
    if image_path_str.startswith('/media/'):
        # /media/を削除してS3パスを取得
        s3_path = image_path_str[7:]  # "/media/"を除去
        if hasattr(settings, 'AWS_STORAGE_BUCKET_NAME') and settings.AWS_STORAGE_BUCKET_NAME:
            # S3 URLを直接生成
            return f"https://{settings.AWS_S3_CUSTOM_DOMAIN}/{s3_path}"
    
    # S3のファイルパス（checkin/で始まる、画像拡張子を含む）かどうかを判定
    if (any(keyword in image_path_str.lower() for keyword in ['jpg', 'jpeg', 'png', 'gif', 'webp']) and 
        ('checkin/' in image_path_str or not image_path_str.startswith('/'))):
        # S3 URLを生成
        if hasattr(settings, 'AWS_STORAGE_BUCKET_NAME') and settings.AWS_STORAGE_BUCKET_NAME:
            return f"https://{settings.AWS_S3_CUSTOM_DOMAIN}/{image_path_str}"
    
    # その他の場合はローカルメディアURL
    return f"{settings.MEDIA_URL}{image_path_str}"


def get_standard_image_url(event_code, image_type):
    """規定画像URLを取得（S3優先、フォールバック対応）"""
    try:
        # S3から規定画像を取得
        from .services.s3_service import S3Service
        s3_service = S3Service()
        s3_url = s3_service.get_standard_image_url(event_code, image_type)
        
        if s3_url:
            return s3_url
        
        # S3に規定画像がない場合はデフォルト画像を返す
        default_images = {
            'goal': 'https://rogaining.sumasen.net/images/gifuRoge/asset/goal.png',
            'start': 'https://rogaining.sumasen.net/images/gifuRoge/asset/start.png',
            'checkpoint': 'https://rogaining.sumasen.net/images/gifuRoge/asset/checkpoint.png',
            'finish': 'https://rogaining.sumasen.net/images/gifuRoge/asset/finish.png',
            'photo_none': 'https://rogaining.sumasen.net/images/gifuRoge/asset/photo_none.png'
        }
        
        return default_images.get(image_type, default_images['photo_none'])
        
    except Exception as e:
        logger.error(f"Error getting standard image: {e}")
        # エラー時はデフォルト画像を返す
        return 'https://rogaining.sumasen.net/images/gifuRoge/asset/photo_none.png'


@api_view(['GET'])
def get_checkins(request, *args, **kwargs):
#def get_checkins(request, zekken_number, event_code):
    try:
        # イベントコードをクエリパラメータから取得

        zekken_number = kwargs['zekken_number']
        if not zekken_number:
            logger.debug(f"=== Zekken_number is required.")
            return Response({"error": "zekken_number is required"}, status=400)

        event_code = kwargs['event_code']
        if not event_code:
            logger.debug(f"=== event_code is required.")
            return Response({"error": "event_code is required"}, status=400)

        logger.debug(f"=== Searching for zekken={zekken_number}, event_code={event_code}")

        # チーム情報を取得
        entry = None
        try:
            # まずzekken_numberで検索（数値の場合）
            if zekken_number.isdigit():
                entry = Entry.objects.select_related('team','event').get(zekken_number=int(zekken_number))
            else:
                # 数値でない場合はzekken_labelで検索
                entry = Entry.objects.select_related('team','event').get(zekken_label=zekken_number)
        except Entry.DoesNotExist:
            # zekken_numberで見つからない場合はzekken_labelで再試行
            try:
                entry = Entry.objects.select_related('team','event').get(zekken_label=zekken_number)
            except Entry.DoesNotExist:
                # 最後にzekken_numberとして再試行（数値以外でもエラーハンドリング）
                try:
                    entry = Entry.objects.select_related('team','event').get(zekken_number=zekken_number)
                except (Entry.DoesNotExist, ValueError):
                    logger.warning(f"Entry not found for zekken: {zekken_number}")
                    entry = None

        # チェックインデータの取得とCP情報の結合
        checkins = GpsCheckin.objects.filter(
            zekken=zekken_number,
            event_code=event_code
        ).order_by('serial_number')
        
        logger.debug(f"=== Found {checkins.count()} checkins")

        data = []
        for c in checkins:
            location = Location2025.objects.filter(cp=c.cp_number,group=event_code).first()
            
            # チェックイン画像を取得
            checkin_image = None
            image_url = None
            
            if entry and location:  # entryとlocationが存在する場合のみ画像を検索
                # location.cpを使用してチェックイン画像を検索
                checkin_image = CheckinImages.objects.filter(
                    team_name=entry.team.team_name,
                    event_code=event_code,
                    cp_number=location.cp  # location.cpを使用
                ).first()
                
                # 画像URLを生成
                if checkin_image and checkin_image.checkinimage:
                    try:
                        # S3対応のget_image_url関数を使用
                        image_url = get_image_url(checkin_image.checkinimage)
                        if image_url:
                            logger.info(f"Found checkin image for CP {location.cp} (sub_loc_id: {location.sub_loc_id}): {image_url}")
                        else:
                            logger.warning(f"Failed to generate URL for checkin image CP {location.cp}")
                    except Exception as e:
                        logger.warning(f"Error accessing checkin image for CP {location.cp}: {str(e)}")
                else:
                    logger.debug(f"No checkin image found for CP {location.cp} (sub_loc_id: {location.sub_loc_id})")
            else:
                if not entry:
                    logger.warning(f"No entry found for zekken {zekken_number}, cannot search for checkin images")
                if not location:
                    logger.warning(f"No location found for CP {c.cp_number}")
            
            formatted_time = None
            if c.checkin_time:
                try:
                    # checkin_timeが文字列の場合はdatetimeに変換
                    if isinstance(c.checkin_time, str):
                        c.checkin_time = datetime.strptime(c.checkin_time, '%Y-%m-%d %H:%M:%S')
                    
                    # JST(+9時間)に変換して時刻フォーマット
                    jst_time = c.checkin_time + timedelta(hours=9)
                    formatted_time = jst_time.strftime('%H:%M:%S')
                    
                except (ValueError, TypeError, AttributeError) as e:
                    logger.error(f"Error formatting checkin_time for checkin {c.id}: {str(e)}")
                    logger.error(f"Raw checkin_time value: {c.checkin_time}")
                    formatted_time = None

            data.append({
                'id': c.id,
                'path_order': c.serial_number,
                'cp_number': c.cp_number,
                'sub_loc_id': location.sub_loc_id if location else f"#{c.cp_number}",
                'location_name': location.location_name if location else None,
                'create_at': formatted_time, #(c.checkin_time + timedelta(hours=9)).strftime('%H:%M:%S') if c.checkin_time else None,
                'validate_location': True,  # デフォルト値として設定
                'points': location.checkin_point if location else 0,  # locationから取得
                'buy_flag': False,  # デフォルト値として設定
                'photos': location.photos if location else None,
                'image_address': image_url,  # CheckinImagesテーブルから取得した画像URL
                'receipt_address': None,  # 現在のモデルには存在しない
                'location_name': location.location_name if location else None,
                'checkin_point': location.checkin_point if location else None,
                'buy_point': location.buy_point if location else 0
            })

        #logger.debug(f"data={data}")
        return Response(data)

    except Exception as e:
        logger.error(f"Error in get_checkins: {str(e)}", exc_info=True)
        return Response(
            {"error": f"Failed to retrieve checkins: {str(e)}"}, 
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@api_view(['POST'])
def update_checkins(request):
    try:
        with transaction.atomic():
            update_base = request.data
            logger.info(f"Processing update data: {update_base}")
            zekken_number = update_base['zekken_number']
            event_code = update_base['event_code']

            # 既存レコードの更新
            for update in update_base['checkins']:
                if 'id' in update and int(update['id']) > 0:
                    try:
                        checkin = GpsCheckin.objects.get(id=update['id'])
                        logger.info(f"Updating existing checkin: {checkin}")

                        # 既存レコードの更新
                        checkin.serial_number = update['order']
                        checkin.buy_flag = update.get('buy_flag', False)
                        checkin.validate_location = update.get('validation', False)
                        checkin.points = update.get('points', 0)
                        # checkin.update_at = timezone.now() チェックイン時刻は更新しない
                        checkin.update_user = request.user.email if request.user.is_authenticated else None
                        checkin.save()
                        logger.info(f"Updated existing checkin result: {checkin}")

                    except GpsCheckin.DoesNotExist:
                        logger.error(f"Checkin with id {update['id']} not found")
                        continue  # エラーを無視して次のレコードの処理を継続

            # 新規レコードの作成
            for update in update_base['checkins']:
                if 'id' in update and int(update['id']) == 0:
                    logger.info(f"Creating new checkin: {update}")
                    try:
                        checkin = GpsCheckin.objects.create(
                            zekken=zekken_number,
                            event_code=event_code,
                            serial_number=update['order'],
                            cp_number=update['cp_number'],
                            validate_location=update.get('validation', False),
                            buy_flag=update.get('buy_flag', False),
                            points=update.get('points', 0),
                            # create_at=timezone.now(), チェックイン時刻は更新しない
                            update_at=timezone.now(),
                            create_user=request.user.email if request.user.is_authenticated else None,
                            update_user=request.user.email if request.user.is_authenticated else None
                        )
                        logger.info(f"Created new checkin: {checkin}")

                    except Exception as e:
                        logger.error(f"Error creating new checkin: {str(e)}")
                        continue  # エラーを無視して次のレコードの処理を継続

            # 更新後のデータを順序付けて取得
            updated_checkins = GpsCheckin.objects.filter(
                zekken=zekken_number,
                event_code=event_code
            ).order_by('serial_number')

            return Response({
                'status': 'success',
                'message': 'Checkins updated successfully',
                'data': [{'id': c.id, 'path_order': c.serial_number} for c in updated_checkins]
            })

    except Exception as e:
        logger.error(f"Error in update_checkins: {str(e)}", exc_info=True)
        return Response(
            {"error": "Failed to update checkins", "detail": str(e)},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@api_view(['POST'])
def update_checkins_old(request):
    try:
        with transaction.atomic():
            update_base = request.data
            logger.info(f"Processing update data: {update_base}")
            zekken_number = update_base['zekken_number']
            event_code = update_base['event_code']

            for update in update_base['checkins']:
                if 'id' in update and int(update['id'])>0:
                    # 既存レコードの更新
                    logger.info(f"Updating existing checkin : {update}")
                    try:
                        checkin = GpsCheckin.objects.get(id=update['id'])
                        logger.info(f"Updating existing checkin: {checkin}")
                        
                        # 既存レコードの更新
                        checkin.serial_number = update['order']
                        checkin.buy_flag = update.get('buy_flag', False)
                        checkin.validate_location = update.get('validation', False)
                        checkin.points = update.get('points', 0)
                        checkin.save()
                        logger.info(f"Updated existing checkin result: {checkin}")
                        
                    except GpsCheckin.DoesNotExist:
                        logger.error(f"Checkin with id {update['id']} not found")
                        return Response(
                            {"error": f"Checkin with id {update['id']} not found"}, 
                            status=status.HTTP_404_NOT_FOUND
                        )
            for update in update_base['checkins']:
                if 'id' in update and int(update['id'])==0:
                    # 新規レコードの作成
                    logger.info("Creating new checkin:{update}")
                    try:
                        checkin = GpsCheckin.objects.create(
                            zekken=update_base['zekken_number'],
                            event_code=update_base['event_code'],

                            serial_number=update['order'],
                            cp_number=update['cp_number'],
                            validate_location=update.get('validation', False),
                            buy_flag=update.get('buy_flag', False),
                            points=update.get('points', 0),
                            create_at=timezone.now(),
                            update_at=timezone.now(),
                            create_user=request.user.email if request.user.is_authenticated else None,
                            update_user=request.user.email if request.user.is_authenticated else None
                        )
                        logger.info(f"Updated existing checkin result: {checkin}")
                    
                    except KeyError as e:
                        logger.error(f"Missing required field: {str(e)}")
                        return Response(
                            {"error": f"Missing required field: {str(e)}"}, 
                            status=status.HTTP_400_BAD_REQUEST
                        )

        return Response({'status': 'success', 'message': 'Checkins updated successfully'})
    
    except Exception as e:
        logger.error(f"Error in update_checkins: {str(e)}", exc_info=True)
        return Response(
            {"error": "Failed to update checkins", "detail": str(e)}, 
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@api_view(['GET'])
def get_photo_list(request):
    print("/get_photo_list")
    
    zekken = request.GET.get('zekken')
    event = request.GET.get('event')

    if not zekken or not event:
        return JsonResponse({
            "status": "ERROR",
            "message": "Missing required parameters: zekken and event"
        }, status=400)
    
    # イベントコードからダブルクォートを削除
    event = event.strip('"')
    zekken = zekken.strip('"')

    zekken_conversion = {
        "MF3-160": "MZ3-160",
        "MF3-161": "MZ3-161",
        "MF3-162": "MZ3-162",
        "MF3-163": "MZ3-163",
        "MF5-170": "MZ5-170",
        "MF5-171": "MZ5-171",
        "MF5-172": "MZ5-172",
        "MF5-173": "MZ5-173",
        "MF5-174": "MZ5-174",
        "MF5-175": "MZ5-175",
        "MF5-176": "MZ5-176",
        "MF5-177": "MZ5-177",
        "MF5-178": "MZ5-178",
        "MF5-179": "MZ5-179"
    }

    if zekken in zekken_conversion:
        zekken = zekken_conversion[zekken]

    photo_list = []
    print(f"Final photo_list length: {len(photo_list)}")

    photo_list.extend(_get_photo_list(zekken, event))
    
    return JsonResponse({
        "status": "OK",
        "photo_list": photo_list,
        "report": _get_final_report(zekken, event)
    })

def _get_final_report( zekken, event_code):
    report = f"https://sumasenrogaining.s3.us-west-2.amazonaws.com/{event_code}/scoreboard/certificate_{zekken}.pdf"
    return report

def _get_photo_list(zekken, event_code):
    photos = []
    try:
        print(f"Query parameters - zekken: {zekken}, event_code: {event_code}")

        checkins = GpsCheckin.objects.filter(zekken=zekken, event_code=event_code)
        # クエリ結果の件数を出力
        print(f"Found {checkins.count()} checkins")

        print(f"SQL Query: {checkins.query}")

        for checkin in checkins:
            print(f"Processing checkin - CP number: {checkin.cp_number}")
            if checkin.image_address:
                # URLオブジェクトかどうかを判定
                if hasattr(checkin.image_address, 'url'):
                    photo_url = checkin.image_address.url
                else:
                    # 文字列の場合、httpで始まるかどうかを確認
                    image_address_str = str(checkin.image_address)
                    if image_address_str.lower().startswith('http'):
                        photo_url = image_address_str
                    else:
                        photo_url = f"http://rogaining.sumasen.net/media/{image_address_str}"
                
                print(f"Found image for CP {checkin.cp_number}: {photo_url}")
                photos.append({
                    "cp_number": checkin.cp_number,
                    "photo_url": photo_url
                })
            else:
                print(f"No image found for CP {checkin.cp_number}")
   
        print(f"Total photos found: {len(photos)}")

    except Exception as e:
        print(f"Error in _get_photo_list: {str(e)}")
    return photos

@api_view(['GET'])
def export_excel(request, zekken_number, event_code):
    temp_dir = None

    try:
        # パラメータを文字列型に変換
        zekken_number = str(zekken_number)
        event_code = str(event_code)

        logger.info(f"Exporting Excel/PDF for zekken_number: {zekken_number}, event_code: {event_code}")

        # 入力値の検証
        if not zekken_number or not event_code:
            logger.error("Missing required parameters")
            return Response(
                {"error": "Both zekken_number and event_code are required"},
                status=status.HTTP_400_BAD_REQUEST
            )

        # docbaseディレクトリのパスを絶対パスで設定
        base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        docbase_path = os.path.join(base_dir, 'docbase')

        # ディレクトリ存在確認と作成
        os.makedirs(docbase_path, exist_ok=True)

        # 設定ファイルのパス
        template_path = os.path.join(docbase_path, 'certificate_template.xlsx')
        ini_path = os.path.join(docbase_path, 'certificate.ini')

        # テンプレートと設定ファイルの存在確認
        if not os.path.exists(template_path):
            logger.error(f"Template file not found: {template_path}")
            return Response(
                {"error": "Excel template file missing"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

        if not os.path.exists(ini_path):
            logger.error(f"INI file not found: {ini_path}")
            return Response(
                {"error": "Configuration file missing"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

        # Docker環境用のデータベース設定を使用
        db_settings = settings.DATABASES['default']

        # 初期化
        variables = {
            "zekken_number": str(zekken_number),
            "event_code": str(event_code),
            "db": str(db_settings['NAME']),
            "username": str(db_settings['USER']),
            "password": str(db_settings['PASSWORD']),
            "host": str(db_settings['HOST']),
            "port": str(db_settings['PORT']),
            "template_path": template_path
        }

        try:
            excel = SumasenExcel(document="certificate", variables=variables, docbase=docbase_path)
            ret = excel.make_report(variables=variables)

            if ret["status"] != True:
                message = ret.get("message", "No message provided")
                logger.error(f"Excelファイル作成失敗 : ret.message={message}")
                return Response(
                    {"error": f"Excel generation failed: {message}"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR
                )

            excel_path = ret.get("filepath")
            if not excel_path or not os.path.exists(excel_path):
                logger.error(f"Output file not found: {excel_path}")
                return Response(
                    {"error": "Generated file not found"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR
                )


            # 一時ディレクトリを作成（ASCII文字のみのパスを使用）
            temp_dir = tempfile.mkdtemp(prefix='lo-')
            logger.debug(f"Created temp directory: {temp_dir}")
    
            # ASCII文字のみの作業ディレクトリを作成
            work_dir = os.path.join(temp_dir, 'work')
            output_dir = os.path.join(temp_dir, 'output')
            os.makedirs(work_dir, exist_ok=True)
            os.makedirs(output_dir, exist_ok=True)
    
            # すべてのディレクトリに適切な権限を設定
            for directory in [temp_dir, work_dir, output_dir]:
                os.chmod(directory, 0o777)
                logger.debug(f"Set permissions for directory: {directory}")
    
            # ASCII文字のみの一時ファイル名を使用
            temp_excel_name = f"certificate_{zekken_number}.xlsx"
            temp_excel_path = os.path.join(work_dir, temp_excel_name)
            
            # 元のExcelファイルを作業ディレクトリにコピー
            shutil.copy2(excel_path, temp_excel_path)
            os.chmod(temp_excel_path, 0o666)
            logger.debug(f"Copied Excel file to: {temp_excel_path}")


            # LibreOffice設定ディレクトリを作成
            libreoffice_config_dir = os.path.join(temp_dir, 'libreoffice')
            os.makedirs(libreoffice_config_dir, exist_ok=True)

            # フォント設定ディレクトリを作成
            font_conf_dir = os.path.join(temp_dir, 'fonts')
            os.makedirs(font_conf_dir, exist_ok=True)

            # フォント設定ファイルを作成
            fonts_conf_content = '''<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
    <dir>/usr/share/fonts</dir>
    <match target="pattern">
        <test qual="any" name="family">
            <string>sans-serif</string>
        </test>
        <edit name="family" mode="assign" binding="same">
            <string>IPAexGothic</string>
        </edit>
    </match>
    <match target="pattern">
        <test qual="any" name="family">
            <string>serif</string>
        </test>
        <edit name="family" mode="assign" binding="same">
            <string>IPAexMincho</string>
        </edit>
    </match>
</fontconfig>'''

            font_conf_path = os.path.join(libreoffice_config_dir, 'fonts.conf')
            with open(font_conf_path, 'w') as f:
                f.write(fonts_conf_content)


            # LibreOfficeのフォント設定を作成
            registry_dir = os.path.join(libreoffice_config_dir, 'registry')
            os.makedirs(registry_dir, exist_ok=True)

            # フォント埋め込み設定を作成
            pdf_export_config = '''<?xml version="1.0" encoding="UTF-8"?>
<oor:data xmlns:oor="http://openoffice.org/2001/registry">
 <oor:component-data oor:package="org.openoffice.Office.Common" oor:name="Filter">
    <node oor:name="PDF">
      <prop oor:name="EmbedFonts" oor:op="fuse">
        <value>true</value>
      </prop>
      <prop oor:name="ExportFormFields" oor:op="fuse">
        <value>true</value>
      </prop>
      <prop oor:name="UseTaggedPDF" oor:op="fuse">
        <value>true</value>
      </prop>
    </node>
 </oor:component-data>
</oor:data>'''

            pdf_config_path = os.path.join(registry_dir, 'pdf_export.xcu')
            with open(pdf_config_path, 'w') as f:
                f.write(pdf_export_config)

            # すべてのディレクトリに適切な権限を設定
            for directory in [temp_dir, work_dir, output_dir,registry_dir]:
                os.chmod(directory, 0o777)
                logger.debug(f"Set permissions for directory: {directory}")

            os.chmod(temp_excel_path, 0o666)
            os.chmod(font_conf_path, 0o666)
            os.chmod(pdf_config_path, 0o666)

            # フォーマット指定（excel or pdf）
            format_type = request.query_params.get('format', 'pdf')

            if format_type.lower() == 'pdf':
                try:
                    # パスとファイル名に分離
                    file_dir = os.path.dirname(temp_excel_path)     # パス部分の取得
                    file_name = os.path.basename(temp_excel_path)   # ファイル名部分の取得

                    # ファイル名の拡張子をpdfに変更
                    base_name = os.path.splitext(file_name)[0]  # 拡張子を除いたファイル名
                    new_file_name = base_name + ".pdf"          # 新しい拡張子を追加

                    # 新しいパスを作成
                    pdf_path = os.path.join(file_dir, new_file_name)

                    logger.info(f"Converting Excel to PDF in directory: {file_dir}")

                    # LibreOfficeを使用してExcelをPDFに変換
                    conversion_command = [
                        'soffice',  # LibreOfficeの代替コマンド
                        '--headless',
                        '--convert-to', 'pdf:writer_pdf_Export',
                        '--outdir',file_dir,
                        '-env:UserInstallation=file://' + libreoffice_config_dir,
                        temp_excel_path
                    ]

                    logger.debug(f"Running conversion command: {' '.join(conversion_command)}")

                    # 環境変数を設定
                    env = os.environ.copy()
                    #env['HOME'] = temp_dir  # LibreOfficeの設定ディレクトリを一時ディレクトリに設定
                    env['HOME'] = temp_dir
                    env['LANG'] = 'ja_JP.UTF-8'  # 日本語環境を設定
                    env['LC_ALL'] = 'ja_JP.UTF-8'
                    env['FONTCONFIG_FILE'] = font_conf_path
                    env['FONTCONFIG_PATH'] = font_conf_dir

                    # 変換プロセスを実行
                    process = subprocess.run(
                        conversion_command,
                        env=env,
                        capture_output=True,
                        text=True,
                        cwd=work_dir,
                        check=True,
                        timeout=30
                    )

                    logger.debug(f"Conversion output: {process.stdout}")

                    # PDFファイルの存在確認
                    if not os.path.exists(pdf_path):
                        logger.error("PDF conversion failed - output file not found")
                        return Response(
                            {"error": "PDF conversion failed - output file not found"},
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR
                        )

                    s3 = S3Bucket('sumasenrogaining')
                    s3.upload_file(pdf_path, f'{event_code}/scoreboard/certificate_{zekken_number}.pdf')
                    s3.upload_file(excel_path, f'{event_code}/scoreboard_excel/certificate_{zekken_number}.xlsx')

                    # PDFファイルを読み込んでレスポンスとして返す
                    with open(pdf_path, 'rb') as pdf_file:
                        pdf_content = pdf_file.read()
                    
                    os.remove(temp_excel_path)
                    os.remove(excel_path)
                    os.remove(pdf_path)

                    # PDFファイルをレスポンスとして返す
                    response = HttpResponse(
                        pdf_content,
                        content_type='application/pdf'
                    )
                    response['Content-Disposition'] = f'inline; filename="certificate_{zekken_number}_{event_code}.pdf"'
                    return response


                except subprocess.CalledProcessError as e:
                    logger.error(f"Error converting to PDF: {str(e)}\nSTDOUT: {e.stdout}\nSTDERR: {e.stderr}")
                    return Response(
                        {"error": f"PDF conversion failed: {str(e)}"},
                        status=status.HTTP_500_INTERNAL_SERVER_ERROR
                        )
                finally:
                    # 一時ディレクトリの削除
                    if temp_dir and os.path.exists(temp_dir):
                        try:
                            shutil.rmtree(temp_dir)
                            logger.debug(f"Temporary directory removed: {temp_dir}")
                        except Exception as e:
                            logger.warning(f"Failed to remove temporary directory: {str(e)}")

            else:  # Excel形式の場合
                with open(excel_path, 'rb') as excel_file:
                    response = HttpResponse(
                        excel_file.read(),
                        content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
                    )
                    response['Content-Disposition'] = f'attachment; filename="certificate_{zekken_number}_{event_code}.xlsx"'
                    return response

        except Exception as e:
            logger.error(f"Error in Excel/PDF generation: {str(e)}", exc_info=True)
            return Response(
                {"error": f"File generation failed: {str(e)}"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    except Exception as e:
        logger.error(f"Error in export_excel: {str(e)}", exc_info=True)
        return Response(
            {"error": "Failed to export file", "detail": str(e)},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )
    finally:
        # 確実に一時ディレクトリを削除
        if temp_dir and os.path.exists(temp_dir):
            try:
                shutil.rmtree(temp_dir)
            except Exception as e:
                logger.warning(f"Failed to remove temporary directory in finally block: {str(e)}")

@api_view(['GET'])
def export_exceli_old2(request,zekken_number, event_code):

    try:
        # パラメータを文字列型に変換
        zekken_number = str(zekken_number)
        event_code = str(event_code)

        logger.info(f"Exporting Excel for zekken_number: {zekken_number}, event_code: {event_code}")

        # 入力値の検証
        if not zekken_number or not event_code:
            logger.error("Missing required parameters")
            return Response(
                {"error": "Both zekken_number and event_code are required"},
                status=status.HTTP_400_BAD_REQUEST
            )

        # docbaseディレクトリのパスを絶対パスで設定
        base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        docbase_path = os.path.join(base_dir, 'docbase')
        
        # ディレクトリ存在確認と作成
        os.makedirs(docbase_path, exist_ok=True)
        
        # 設定ファイルのパス
        template_path = os.path.join(docbase_path, 'certificate_template.xlsx')
        ini_path = os.path.join(docbase_path, 'certificate.ini')
 
         # テンプレートと設定ファイルの存在確認
        if not os.path.exists(template_path):
            logger.error(f"Template file not found: {template_path}")
            return Response(
                {"error": "Excel template file missing"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

        if not os.path.exists(ini_path):
            logger.error(f"INI file not found: {ini_path}")
            return Response(
                {"error": "Configuration file missing"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

        # Docker環境用のデータベース設定を使用
        db_settings = settings.DATABASES['default']



        # 初期化
        variables = {
            "zekken_number":str(zekken_number),
            "event_code":str(event_code),
            "db":str(db_settings['NAME']),  #"rogdb",
            "username":str(db_settings['USER']), #"admin",
            "password":str(db_settings['PASSWORD']), #"admin123456",
            "host":str(db_settings['HOST']),  # Docker Composeのサービス名を使用 # "localhost",
            "port":str(db_settings['PORT']),  #"5432",
            "template_path": template_path
        }

        # データベース接続情報のログ出力（パスワードは除く）
        logger.info(f"Attempting database connection to {variables['host']}:{variables['port']} "
                   f"with user {variables['username']} and database {variables['db']}")

        try:
            excel = SumasenExcel(document="certificate", variables=variables, docbase=docbase_path)
            # ./docbase/certificate.ini の定義をベースに、
            # ./docbase/certificate_template.xlsxを読み込み
            # ./docbase/certificate_(zekken_number).xlsxを作成する
        
            # シート初期化
            logger.info("Generating report with variables: %s",
                 {k: v for k, v in variables.items() if k != 'password'})  # パスワードを除外
    
            ret = excel.make_report(variables=variables)
            if ret["status"]==True:
                filepath=ret["filepath"]
                logging.info(f"Excelファイル作成 : ret.filepath={filepath}")
            else:
                message = ret.get("message", "No message provided")
                logging.error(f"Excelファイル作成失敗 : ret.message={message}")
        
            output_path = ret.get("filepath")   
            if not output_path or not os.path.exists(output_path):
                logger.error(f"Output file not found: {output_path}")
                return Response(
                    {"error": "Generated file not found"},
                    status=status.HTTP_500_INTERNAL_SERVER_ERROR
                )
            excel_path = output_path

            # PDFのファイル名を生成
            pdf_filename = f'certificate_{zekken_number}_{event_code}.pdf'
            pdf_path = os.path.join(docbase_path, pdf_filename)

            # フォーマット指定（excel or pdf）
            format_type = request.query_params.get('format', 'pdf') # 'excel'

            if format_type.lower() == 'pdf':
                try:
                    # 一時ディレクトリを作成
                    temp_dir = tempfile.mkdtemp()
                    temp_excel = os.path.join(temp_dir, f'certificate_{zekken_number}.xlsx')
                    temp_pdf = os.path.join(temp_dir, f'certificate_{zekken_number}.pdf')

                    # Excelファイルを一時ディレクトリにコピー
                    shutil.copy2(excel_path, temp_excel)

                    # 一時ディレクトリのパーミッションを設定
                    os.chmod(temp_dir, 0o777)
                    os.chmod(temp_excel, 0o666)

                    logger.info(f"Converting Excel to PDF in temp directory: {temp_dir}")
  

                    # LibreOfficeを使用してExcelをPDFに変換
                    conversion_command = [
                        'soffice',
                        '--headless',
                        '--convert-to',
                        'pdf',
                        '--outdir',
                        temp_dir,
                        temp_excel
                    ]

                    logger.debug(f"Running conversion command: {' '.join(conversion_command)}")

                    # 環境変数を設定
                    env = os.environ.copy()
                    env['HOME'] = temp_dir  # LibreOfficeの設定ディレクトリを一時ディレクトリに設定

                    # 変換プロセスを実行
                    process = subprocess.run(
                        conversion_command,
                        env=env,
                        capture_output=True,
                        text=True,
                        check=True
                    )
                    
                    logger.debug(f"Conversion output: {process.stdout}")

                    # PDFファイルの存在確認
                    if not os.path.exists(temp_pdf):
                        logger.error("PDF conversion failed - output file not found")
                        return Response(
                            {"error": "PDF conversion failed - output file not found"},
                            status=status.HTTP_500_INTERNAL_SERVER_ERROR
                        )

                    # PDFファイルを読み込んでレスポンスを返す
                    with open(temp_pdf, 'rb') as pdf_file:
                        pdf_content = pdf_file.read()
                        
                    response = HttpResponse(pdf_content, content_type='application/pdf')
                    response['Content-Disposition'] = f'attachment; filename="certificate_{zekken_number}_{event_code}.pdf"'
                    return response


                except subprocess.CalledProcessError as e:
                    logger.error(f"Error converting to PDF: {str(e)}\nSTDOUT: {e.stdout}\nSTDERR: {e.stderr}")
                    return Response(
                        {"error": "PDF conversion failed"},
                        status=status.HTTP_500_INTERNAL_SERVER_ERROR
                    )
                finally:
                    # 一時ディレクトリの削除
                    if temp_dir and os.path.exists(temp_dir):
                        try:
                            shutil.rmtree(temp_dir)
                            logger.debug(f"Temporary directory removed: {temp_dir}")
                        except Exception as e:
                            logger.warning(f"Failed to remove temporary directory: {str(e)}")


            else:  # Excel形式の場合
                with open(excel_path, 'rb') as excel_file:
                    response = HttpResponse(
                        excel_file.read(),
                        content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
                    )
                    excel_filename = f'certificate_{zekken_number}_{event_code}.xlsx'
                    response['Content-Disposition'] = f'attachment; filename="{excel_filename}"'
                    return response

        except Exception as e:
            logger.error(f"Error in Excel/PDF generation: {str(e)}", exc_info=True)
            return Response(
                {"error": f"File generation failed: {str(e)}"},
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    except Exception as e:
        logger.error(f"Error in export_excel: {str(e)}", exc_info=True)
        return Response(
            {"error": "Failed to export file", "detail": str(e)},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )
    finally:
        # 確実に一時ディレクトリを削除
        if temp_dir and os.path.exists(temp_dir):
            try:
                shutil.rmtree(temp_dir)
            except Exception as e:
                logger.warning(f"Failed to remove temporary directory in finally block: {str(e)}")


# ----- for Supervisor -----

@api_view(['GET'])
def test_api(request):
    logger.debug("Test API endpoint called")
    return JsonResponse({"status": "API is working"})

@api_view(['GET'])
#@authentication_classes([TokenAuthentication])
#@permission_classes([IsAuthenticated])
def get_goalimage(request):
    """
    ゼッケン番号とイベントコードに基づいてゴール画像情報を取得するエンドポイント
    
    Parameters:
        zekken_number (str): ゼッケン番号
        event_code (str): イベントコード
    
    Returns:
        Response: ゴール画像情報を含むJSONレスポンス
    """
    try:
        logger.debug(f"get_goalimage called with params: {request.GET}")

        # リクエストパラメータを取得
        zekken_number = request.GET.get('zekken_number')
        event_code = request.GET.get('event_code')

        logger.debug(f"Searching for goal records with zekken_number={zekken_number}, event_code={event_code}")

        # パラメータの検証
        if not zekken_number or not event_code:
            return Response(
                {"error": "zekken_number and event_code are required"},
                status=status.HTTP_400_BAD_REQUEST
            )

        # ゴール画像レコードを検索（最も早いゴール時間のレコードを取得）
        goal_records = GoalImages.objects.filter(
            zekken_number=zekken_number,
            event_code=event_code
        ).order_by('goaltime')

        logger.debug(f"Found {goal_records.count()} goal records")

        if not goal_records.exists():
            return Response(
                {"message": "No goal records found"},
                status=status.HTTP_404_NOT_FOUND
            )

        # 最も早いゴール時間のレコード（cp_number = 0）を探す
        valid_goal = goal_records.filter(cp_number=0).first()
        
        if not valid_goal:
            return Response(
                {"message": "No valid goal record found"},
                status=status.HTTP_404_NOT_FOUND
            )

        # シリアライザでデータを整形
        serializer = GolaImageSerializer(valid_goal)
        
        # レスポンスデータの構築
        response_data = {
            "goal_record": serializer.data,
            "total_records": goal_records.count(),
            "has_multiple_records": goal_records.count() > 1
        }

        logger.info(f"Retrieved goal record for zekken_number {zekken_number} in event {event_code}")
        return Response(response_data)

    except Exception as e:
        logger.error(f"Error retrieving goal record: {str(e)}", exc_info=True)
        return Response(
            {"error": f"Failed to retrieve goal record: {str(e)}"},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@api_view(['POST'])
#@authentication_classes([TokenAuthentication])
#@permission_classes([IsAuthenticated])
def update_goal_time(request):
    try:
        logger.info(f"update_goal_time:{request}")
        # リクエストからデータを取得
        zekken_number = request.data.get('zekken_number')
        event_code = request.data.get('event_code')
        team_name = request.data.get('team_name')
        goal_time_str = request.data.get('goaltime')

        logger.info(f"zekken_number={zekken_number},event_code={event_code},team_name={team_name},goal_time={goal_time_str}")

        # 入力バリデーション
        #if not all([zekken_number, event_code, team_name, goal_time_str]):
        #    return Response(
        #        {"error": "Missing required fields"},
        #        status=status.HTTP_400_BAD_REQUEST
        #    )

        try:
            # 文字列からdatetimeオブジェクトに変換
            goal_time = datetime.strptime(goal_time_str, '%Y-%m-%dT%H:%M:%S')
        except ValueError:
            return Response(
                {"error": "Invalid goal time format"},
                status=status.HTTP_400_BAD_REQUEST
            )

        # 既存のゴール記録を探す
        goal_record = GoalImages.objects.filter(
            team_name=team_name,
            event_code=event_code
        ).first()

        if goal_record:
            # 既存の記録を更新
            goal_record.goaltime = goal_time
            goal_record.zekken_number = zekken_number
            goal_record.cp_number = -2
            goal_record.save()
            logger.info(f"Updated goal time as {goal_time} for team {team_name} in event {event_code}")
        else:
            # 新しい記録を作成
            entry = Entry.objects.get(zekken_number=zekken_number, event__event_name=event_code)
            GoalImages.objects.create(
                user=entry.owner,
                goaltime=goal_time,
                team_name=team_name,
                event_code=event_code,
                cp_number=-2,  # ゴール地点を表すCP番号
                zekken_number = zekken_number
            )
            logger.info(f"Created new goal time record for team {team_name} in event {event_code}")

        return Response({"message": "Goal time updated successfully"})

    except Entry.DoesNotExist:
        logger.error(f"Entry not found for zekken_number {zekken_number} in event {event_code}")
        return Response(
            {"error": "Entry not found"},
            status=status.HTTP_404_NOT_FOUND
        )
    except Exception as e:
        logger.error(f"Error updating goal time: {str(e)}")
        return Response(
            {"error": f"Failed to update goal time: {str(e)}"},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )



def get_team_status(last_checkin_time, goal_time, event_end_time):
    """
    チームの状態を判定する
    """
    now = timezone.now()
    
    if goal_time:
        return "ゴール"
    
    if not last_checkin_time:
        if now > event_end_time + timedelta(minutes=30):
            return "棄権"
        return "競技中"
        
    # 最終チェックインから30分以上経過
    if now > last_checkin_time + timedelta(minutes=30):
        return "棄権"
        
    return "競技中"

def calculate_late_points(goal_time, event_end_time):
    """遅刻による減点を計算する"""
    if not goal_time or not event_end_time:
        return 0
    
    minutes_late = max(0, int((goal_time - event_end_time).total_seconds() / 60))
    return minutes_late * -50

def is_disqualified(start_time, goal_time, duration):
    """失格判定を行う"""
    if not goal_time or not start_time or not duration:
        return False  # ゴール時間がない場合は失格としない（競技中の可能性）
    
    # duration（timedelta）に15分を加算
    max_time = start_time + duration + timedelta(minutes=15)
    return goal_time > max_time

@api_view(['GET'])
def get_ranking(request, event_code, category_name):
    """特定のイベントとクラスのランキングを取得する"""
    try:
        # イベントの情報を取得
        event = NewEvent2.objects.get(event_name=event_code)
        
        # 有効なエントリーを取得
        entries = Entry.objects.filter(
            event=event,
            category__category_name=category_name,
            is_active=True
        ).select_related('team', 'category')

        rankings = []
        disqualified = []  # 失格チームのリスト

        for entry in entries:
            # チェックインポイントを集計
            checkins = GpsCheckin.objects.filter(
                zekken_number=entry.zekken_number,
                event_code=event_code
            ).aggregate(
                total_points=Sum('points')
            )

            # 最後のチェックイン時刻を取得
            last_checkin = GpsCheckin.objects.filter(
                zekken_number=entry.zekken_number,
                event_code=event_code
            ).order_by('-create_at').first()

            last_checkin_time = last_checkin.create_at if last_checkin else None

            # ゴール時間を取得 (最も早いゴール時間)
            goal_record = GoalImages.objects.filter(
                zekken_number=entry.zekken_number,
                event_code=event_code
            ).order_by('goaltime').first()

            goal_time = goal_record.goaltime if goal_record else None
            total_points = checkins['total_points'] or 0

            # イベントの終了予定時刻を計算
            expected_end_time = event.start_datetime + entry.category.duration

            # チーム状態の判定
            team_status = get_team_status(last_checkin_time, goal_time, expected_end_time)

            # 失格判定
            if is_disqualified(event.start_datetime, goal_time, entry.category.duration):
                disqualified.append({
                    'team_name': entry.team.team_name,
                    'zekken_number': entry.zekken_number,
                    'point': total_points,
                    'late_point': 0,
                    'goal_time': goal_time,
                    'reason': '制限時間超過',
                    'status': team_status
                })
                continue

            # 遅刻減点を計算
            late_points = calculate_late_points(goal_time, expected_end_time)

            rankings.append({
                'team_name': entry.team.team_name,
                'zekken_number': entry.zekken_number,
                'point': total_points,
                'late_point': abs(late_points),
                'final_point': total_points + late_points,
                'goal_time': goal_time,
                'status': team_status,
                'last_checkin': last_checkin_time
            })

        # ポイントの高い順（同点の場合はゴール時間が早い順）にソート
        # 棄権チームを最後に
        rankings.sort(key=lambda x: (
            -1 if x['status'] != '棄権' else 0,  # 棄権でないチームを優先
            -x['final_point'],  # 得点の高い順
            x['goal_time'] or datetime.max  # ゴール時間の早い順
        ))

        # 有効なランキングと失格チームを結合
        final_rankings = {
            'rankings': rankings,
            'disqualified': disqualified
        }

        return Response(final_rankings)

    except NewEvent2.DoesNotExist:
        return Response(
            {"error": "Specified event not found"},
            status=status.HTTP_404_NOT_FOUND
        )
    except Exception as e:
        logger.error(f"Error in get_ranking: {str(e)}", exc_info=True)
        return Response(
            {"error": str(e)},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )

@api_view(['GET'])
def all_ranking_top3(request, event_code):
    """特定のイベントの全クラスのTOP3を取得する"""
    try:
        event_code = unquote(event_code)  # URLデコード
        # イベントの情報を取得
        event = NewEvent2.objects.get(event_name=event_code)

        # イベントの有効なカテゴリーを取得
        categories = NewCategory.objects.filter(
            entry__event=event,
            entry__is_active=True
        ).distinct()

        rankings = {}
        for category in categories:
            # カテゴリーごとのエントリーを取得
            entries = Entry.objects.filter(
                event=event,
                category=category,
                is_active=True
            ).select_related('team')

            category_rankings = []
            disqualified = []  # カテゴリーごとの失格チーム

            for entry in entries:
                # チェックインポイントを集計
                checkins = GpsCheckin.objects.filter(
                    zekken_number=entry.zekken_number,
                    event_code=event_code
                ).aggregate(
                    total_points=Sum('points')
                )

                # 最後のチェックイン時刻を取得
                last_checkin = GpsCheckin.objects.filter(
                    zekken_number=entry.zekken_number,
                    event_code=event_code
                ).order_by('-create_at').first()

                last_checkin_time = last_checkin.create_at if last_checkin else None

                # ゴール時間を取得
                goal_record = GoalImages.objects.filter(
                    zekken_number=entry.zekken_number,
                    event_code=event_code
                ).order_by('goaltime').first()

                goal_time = goal_record.goaltime if goal_record else None
                total_points = checkins['total_points'] or 0

                # イベントの終了予定時刻を計算
                expected_end_time = event.start_datetime + category.duration

                # チーム状態の判定
                team_status = get_team_status(last_checkin_time, goal_time, expected_end_time)

                # 失格判定
                if is_disqualified(event.start_datetime, goal_time, entry.category.duration):
                    disqualified.append({
                        'team_name': entry.team.team_name,
                        'zekken_number': entry.zekken_number,
                        'point': total_points,
                        'late_point': 0,
                        'goal_time': goal_time,
                        'reason': '制限時間超過',
                        'status': team_status
                    })
                    continue

                # 遅刻減点を計算
                late_points = calculate_late_points(goal_time, expected_end_time)

                category_rankings.append({
                    'team_name': entry.team.team_name,
                    'zekken_number': entry.zekken_number,
                    'point': total_points,
                    'late_point': abs(late_points),
                    'final_point': total_points + late_points,
                    'goal_time': goal_time,
                    'status': team_status,
                    'last_checkin': last_checkin_time
                })

            # ポイントの高い順（同点の場合はゴール時間が早い順）にソート
            # 棄権チームを最後に
            category_rankings.sort(key=lambda x: (
                -1 if x['status'] != '棄権' else 0,  # 棄権でないチームを優先
                -x['final_point'],  # 得点の高い順
                x['goal_time'] or datetime.max  # ゴール時間の早い順
            ))

            # TOP3のみを保持（棄権を除く）
            top_rankings = [r for r in category_rankings if r['status'] != '棄権'][:3]

            rankings[category.category_name] = {
                'rankings': top_rankings,
                'disqualified': disqualified
            }

        return Response(rankings)

    except NewEvent2.DoesNotExist:
        return Response(
            {"error": "Specified event not found"},
            status=status.HTTP_404_NOT_FOUND
        )
    except Exception as e:
        logger.error(f"Error in all_ranking_top3: {str(e)}", exc_info=True)
        return Response(
            {"error": str(e)},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )

# ルートページ用のビュー
def index_view(request):
    """ルートページをスーパーバイザー画面にリダイレクト"""
    from django.shortcuts import render
    from django.http import HttpResponse
    
    # supervisor/html/index.htmlを読み込んで返す
    try:
        import os
        base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        supervisor_path = os.path.join(base_dir, 'supervisor', 'html', 'index.html')
        
        with open(supervisor_path, 'r', encoding='utf-8') as file:
            content = file.read()
        
        return HttpResponse(content, content_type='text/html')
    except Exception as e:
        logger.error(f"Error loading supervisor page: {str(e)}")
        return HttpResponse(
            "<h1>System Error</h1><p>Failed to load supervisor interface</p>",
            status=500
        )

# Import LocationCheckinView for evaluation_value-based interactions
from .location_checkin_view import LocationCheckinView

def location_checkin_test(request):
    """ロケーションチェックインのテストページ"""
    from django.shortcuts import render
    return render(request, 'location_checkin_test.html')
