from django.contrib.auth.hashers import make_password
from dataclasses import field
import email
from enum import unique
from pyexpat import model
from sre_constants import CH_LOCALE
from typing import ChainMap
from django.contrib.gis.db import models
from django.contrib.postgres.fields import ArrayField
from django.utils import timezone
from datetime import timedelta
try:
    from django.db.models import JSONField
except ImportError:
    from django.contrib.postgres.fields import JSONField
from django.utils.translation import gettext_lazy as _
from django.contrib.auth.models import User
from django.db.models.signals import post_save, post_delete, pre_save
from django.dispatch import receiver
import geopandas as gpd
from sqlalchemy import *
from geoalchemy2 import Geometry, WKTElement
import os, zipfile, glob
import environ
from geo.Postgres import Db
from sqlalchemy.sql.functions import mode
from .mapping import location_mapping, location_line_mapping, location_polygon_mapping
from .choices import LAYER_CHOICES
from django.contrib.gis.utils import LayerMapping
from django.apps import apps
from django.db import transaction
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager
from django.contrib.postgres.indexes import GistIndex
from django.utils import timezone
from datetime import timedelta,date

from django.contrib.gis.geos import Point,MultiPoint

#from django.db import models
from django.core.exceptions import ValidationError

import csv
import codecs
import sys
import time
import uuid
import logging

logger = logging.getLogger(__name__)

env = environ.Env(DEBUG=(bool, False))
environ.Env.read_env(env_file=".env")

db = Db(dbname=env("POSTGRES_DBNAME"), user=env("POSTGRES_USER"), password=env("POSTGRES_PASS"), host=env("PG_HOST"), port=env("PG_PORT"))


def get_file_path(instance, filename):
    ext = filename.split('.')[-1]
    filename = "%s/%s.%s" %  (uuid.uuid4(), uuid.uuid4(), ext)
    return os.path.join('uploads/geoms', filename)


def remove_bom_inplace(path):
    """Removes BOM mark, if it exists, from a file and rewrites it in-place"""
    buffer_size = 4096
    bom_length = len(codecs.BOM_UTF8)
 
    with open(path, mode="r+b") as fp:
        chunk = fp.read(buffer_size)
        if chunk.startswith(codecs.BOM_UTF8):
            i = 0
            chunk = chunk[bom_length:]
            while chunk:
                fp.seek(i)
                fp.write(chunk)
                i += len(chunk)
                fp.seek(bom_length, os.SEEK_CUR)
                chunk = fp.read(buffer_size)
            fp.seek(-bom_length, os.SEEK_CUR)
            fp.truncate()

class GifurogeRegister(models.Model):
    event_code = models.CharField(max_length=100)
    time = models.IntegerField(choices=[(3, '3時間'), (5, '5時間')])
    owner_name_kana = models.CharField(max_length=100)
    owner_name = models.CharField(max_length=100)
    email = models.EmailField()
    password = models.CharField(max_length=100)
    owner_birthday = models.DateField(blank=True,null=True)
    owner_sex = models.CharField(max_length=10,blank=True,null=True)
    team_name = models.CharField(max_length=100)
    department = models.CharField(max_length=100)
    members_count = models.IntegerField()
    member2 = models.CharField(max_length=100, blank=True, null=True)
    birthday2 = models.DateField(blank=True,null=True)
    sex2 = models.CharField(max_length=10,blank=True,null=True)
    member3 = models.CharField(max_length=100, blank=True, null=True)
    birthday3 = models.DateField(blank=True,null=True)
    sex3 = models.CharField(max_length=10,blank=True,null=True)
    member4 = models.CharField(max_length=100, blank=True, null=True)
    birthday4 = models.DateField(blank=True,null=True)
    sex4 = models.CharField(max_length=10,blank=True,null=True)
    member5 = models.CharField(max_length=100, blank=True, null=True)
    birthday5 = models.DateField(blank=True,null=True)
    sex5 = models.CharField(max_length=10,blank=True,null=True)


class CustomUserManager(BaseUserManager):
    
    def create_user(self, email, password=None, **other_fields):
        if not email:
            raise ValueError(_("You must provide an email address"))
        email = self.normalize_email(email)
        user = self.model(email=email, **other_fields)
        user.set_password(password)
        user.save(using=self._db)

        return user

    def create_superuser(self, email, password, group, event_code=None, team_name=None, **other_fields):
        # Providing default values for event_code and team_name if they are not provided
        if event_code is None:
            event_code = 'test'  # replace this with some default value
        if team_name is None:
            team_name = 'test'  # replace this with some default value

        other_fields.setdefault('is_staff', True)
        other_fields.setdefault('is_superuser', True)
        other_fields.setdefault('is_active', True)

        if other_fields.get('is_staff') is not True:
            raise ValueError(_('Superuser must be assigned to staff'))
        if other_fields.get('is_superuser') is not True:
            raise ValueError(_('Superuser must have is_superuser set to True'))

        return self.create_user(email, password, **other_fields)



class JpnAdminMainPerf(models.Model):
    geom = models.MultiPolygonField(blank=True, null=True)
    adm0_en = models.CharField(max_length=254, blank=True, null=True)
    adm0_ja = models.CharField(max_length=254, blank=True, null=True)
    adm0_pcode = models.CharField(max_length=254, blank=True, null=True)
    adm1_en = models.CharField(max_length=254, blank=True, null=True)
    adm1_ja = models.CharField(max_length=254, blank=True, null=True)
    adm1_pcode = models.CharField(max_length=254, blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'jpn_admin_main_perf'
        indexes = [
            models.Index(fields=['geom'], name='jpn_admin_main_perf_geom_idx'),
            # Add other fields for indexing as per the requirements
        ]


# class JpnAdminPerf(models.Model):
#     geom = models.MultiLineStringField(blank=True, null=True)
#     et_id = models.IntegerField(blank=True, null=True)
#     et_right = models.CharField(max_length=80, blank=True, null=True)
#     et_left = models.CharField(max_length=80, blank=True, null=True)
#     adm2_l = models.CharField(max_length=50, blank=True, null=True)
#     adm1_l = models.CharField(max_length=50, blank=True, null=True)
#     adm0_l = models.CharField(max_length=50, blank=True, null=True)
#     adm0_r = models.CharField(max_length=50, blank=True, null=True)
#     adm1_r = models.CharField(max_length=50, blank=True, null=True)
#     adm2_r = models.CharField(max_length=50, blank=True, null=True)
#     admlevel = models.IntegerField(blank=True, null=True)

#     class Meta:
#         managed = False
#         db_table = 'jpn_admin_perf'
#         indexes = [
#             models.Index(fields=['geom'], name='jpn_admin_perf_geom_idx'),
#             # Add other fields for indexing as per the requirements
#         ]

# ###
# ### Cities
# ###
class JpnSubPerf(models.Model):
     geom = models.MultiPolygonField(blank=True, null=True)
     adm0_en = models.CharField(max_length=254, blank=True, null=True)
     adm0_ja = models.CharField(max_length=254, blank=True, null=True)
     adm0_pcode = models.CharField(max_length=254, blank=True, null=True)
     adm1_en = models.CharField(max_length=254, blank=True, null=True)
     adm1_ja = models.CharField(max_length=254, blank=True, null=True)
     adm1_pcode = models.CharField(max_length=254, blank=True, null=True)
     adm2_ja = models.CharField(max_length=254, blank=True, null=True)
     adm2_en = models.CharField(max_length=254, blank=True, null=True)
     adm2_pcode = models.CharField(max_length=254, blank=True, null=True)
     name_modified = models.CharField(max_length=254, blank=True, null=True)
     area_name = models.CharField(max_length=254, blank=True, null=True)
     list_order =models.IntegerField(default=0)

     class Meta:
         managed = False
         db_table = 'jpn_sub_perf'
         indexes = [
             models.Index(fields=['geom'], name='jpn_sub_perf_geom_idx'),
             # Add other fields for indexing as per the requirements
        ]

###
### Gifu Areas
###
class GifuAreas(models.Model):
    geom = models.MultiPolygonField(blank=True, null=True)
    adm0_en = models.CharField(max_length=254, blank=True, null=True)
    adm0_ja = models.CharField(max_length=254, blank=True, null=True)
    adm0_pcode = models.CharField(max_length=254, blank=True, null=True)
    adm1_en = models.CharField(max_length=254, blank=True, null=True)
    adm1_ja = models.CharField(max_length=254, blank=True, null=True)
    adm1_pcode = models.CharField(max_length=254, blank=True, null=True)
    adm2_ja = models.CharField(max_length=254, blank=True, null=True)
    adm2_en = models.CharField(max_length=254, blank=True, null=True)
    adm2_pcode = models.CharField(max_length=254, blank=True, null=True)
    area_nm = models.CharField(max_length=254, blank=True, null=True)

    class Meta:
        managed = False
        db_table = 'gifu_areas'
        indexes = [
            models.Index(fields=['geom'], name='gifu_areas_geom_idx'),
            # Add other fields for indexing as per the requirements
        ]


class UserUpload(models.Model):
    name = models.CharField(_("User uploads"), max_length=255)
    file = models.FileField(upload_to=get_file_path, blank=True)
    uploaded_date = models.DateField(auto_now_add=True)

    def __str__(self):
        return self.name

class UserUploadUser(models.Model):
    userfile=models.CharField(_('User file'), max_length=2048 ,blank=True, null=True)
    email=models.CharField(_('User Email'), max_length=255)


class CustomUser(AbstractBaseUser, PermissionsMixin):
    class Groups(models.TextChoices):
        GB1 = '大垣-初心者', '大垣-初心者'
        GB2 = '大垣-３時間', '大垣-３時間'
        GB3 = '大垣-5時間', '大垣-5時間'

    email = models.EmailField(unique=True)
    firstname = models.CharField(max_length=255,blank=True, null=True)
    lastname = models.CharField(max_length=255, blank=True, null=True)
    date_of_birth = models.DateField(blank=True, null=True)
    female = models.BooleanField(default=False)
    group = models.CharField(max_length=255,blank=True)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    date_joined = models.DateTimeField(default=timezone.now)

    is_rogaining = models.BooleanField(default=False)
    zekken_number = models.CharField(_("Zekken Number"), max_length=255, blank=True, null=True)
    event_code = models.CharField(_("Event Code"), max_length=255, blank=True, null=True)
    team_name = models.CharField(_("Team Name"), max_length=255, blank=True, null=True)


    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    objects = CustomUserManager()

    def set_password(self, raw_password):
        self.password = make_password(raw_password)
        self._password = raw_password

    def __str__(self):
        return self.email

class TempUser(models.Model):
    email = models.EmailField(unique=True)
    password = models.CharField(max_length=128)
    is_rogaining = models.BooleanField(default=False)
    zekken_number = models.CharField(max_length=255, blank=True, null=True)
    event_code = models.CharField(max_length=255, blank=True, null=True)
    team_name = models.CharField(max_length=255, blank=True, null=True)
    group = models.CharField(max_length=255)
    firstname = models.CharField(max_length=255,blank=True, null=True)
    lastname = models.CharField(max_length=255, blank=True, null=True)
    date_of_birth = models.DateField(blank=True, null=True)
    female = models.BooleanField(default=False)
    verification_code = models.UUIDField(default=uuid.uuid4, editable=False)
    created_at = models.DateTimeField(auto_now_add=True)
    expires_at = models.DateTimeField()

    def set_password(self, raw_password):
        self.password = make_password(raw_password)

    def check_password(self, raw_password):
        return check_password(raw_password, self.password)

    # TempUserの作成時にこのメソッドを使用
    @classmethod
    def create_temp_user(cls, email, password, **kwargs):
        temp_user = cls(email=email, **kwargs)
        temp_user.set_password(password)
        temp_user.save()
        return temp_user

    def __str__(self):
        return self.email

    def save(self, *args, **kwargs):
        if not self.expires_at:
            self.expires_at = timezone.now() + timedelta(hours=24)  # 24時間の有効期限
        super().save(*args, **kwargs)

    def is_valid(self):
        return timezone.now() <= self.expires_at


class AppVersion(models.Model):
    """アプリバージョン管理モデル"""
    
    PLATFORM_CHOICES = [
        ('android', 'Android'),
        ('ios', 'iOS'),
    ]
    
    version = models.CharField(max_length=20, help_text="セマンティックバージョン (1.2.3)")
    platform = models.CharField(max_length=10, choices=PLATFORM_CHOICES)
    build_number = models.CharField(max_length=20, blank=True, null=True)
    is_latest = models.BooleanField(default=False, help_text="最新版フラグ")
    is_required = models.BooleanField(default=False, help_text="強制更新フラグ")
    update_message = models.TextField(blank=True, null=True, help_text="ユーザー向け更新メッセージ")
    download_url = models.URLField(blank=True, null=True, help_text="アプリストアURL")
    release_date = models.DateTimeField(default=timezone.now)
    created_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        db_table = 'app_versions'
        unique_together = ['version', 'platform']
        indexes = [
            models.Index(fields=['platform'], name='idx_app_versions_platform'),
            models.Index(
                fields=['is_latest'], 
                condition=models.Q(is_latest=True),
                name='idx_app_versions_latest_true'
            ),
        ]
        
    def __str__(self):
        return f"{self.platform} {self.version}"
    
    def save(self, *args, **kwargs):
        """最新版フラグが設定された場合、同一プラットフォームの他のバージョンを非最新にする"""
        if self.is_latest:
            AppVersion.objects.filter(
                platform=self.platform,
                is_latest=True
            ).exclude(pk=self.pk).update(is_latest=False)
        super().save(*args, **kwargs)
    
    @classmethod
    def compare_versions(cls, version1, version2):
        """セマンティックバージョンの比較"""
        def version_tuple(v):
            return tuple(map(int, v.split('.')))
        
        v1 = version_tuple(version1)
        v2 = version_tuple(version2)
        
        if v1 < v2:
            return -1
        elif v1 > v2:
            return 1
        else:
            return 0
    
    @classmethod
    def get_latest_version(cls, platform):
        """指定プラットフォームの最新バージョンを取得"""
        try:
            return cls.objects.filter(platform=platform, is_latest=True).first()
        except cls.DoesNotExist:
            return None


class CheckinExtended(models.Model):
    """チェックイン拡張情報モデル"""
    
    VALIDATION_STATUS_CHOICES = [
        ('pending', 'Pending'),
        ('approved', 'Approved'),
        ('rejected', 'Rejected'),
        ('requires_review', 'Requires Review'),
    ]
    
    gpslog = models.ForeignKey('GpsCheckin', on_delete=models.CASCADE, related_name='extended_info')
    
    # GPS拡張情報
    gps_latitude = models.DecimalField(max_digits=10, decimal_places=8, null=True, blank=True)
    gps_longitude = models.DecimalField(max_digits=11, decimal_places=8, null=True, blank=True)
    gps_accuracy = models.DecimalField(max_digits=6, decimal_places=2, null=True, blank=True, help_text="GPS精度（メートル）")
    gps_timestamp = models.DateTimeField(null=True, blank=True)
    
    # カメラメタデータ
    camera_capture_time = models.DateTimeField(null=True, blank=True)
    device_info = models.TextField(blank=True, null=True)
    
    # 審査・検証情報
    validation_status = models.CharField(
        max_length=20,
        choices=VALIDATION_STATUS_CHOICES,
        default='pending'
    )
    validation_comment = models.TextField(blank=True, null=True)
    validated_by = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True, blank=True)
    validated_at = models.DateTimeField(null=True, blank=True)
    
    # スコア情報
    bonus_points = models.IntegerField(default=0)
    scoring_breakdown = JSONField(default=dict, blank=True)
    
    # システム情報
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'rog_checkin_extended'
        indexes = [
            models.Index(fields=['validation_status'], name='idx_checkin_ext_valid'),
            models.Index(fields=['created_at'], name='idx_checkin_ext_created'),
        ]
    
    def __str__(self):
        return f"CheckinExtended {self.gpslog_id} - {self.validation_status}"


class UploadedImage(models.Model):
    """画像アップロード管理モデル - マルチアップロード対応"""
    
    UPLOAD_SOURCE_CHOICES = [
        ('direct', 'Direct'),
        ('sharing_intent', 'Sharing Intent'),
        ('bulk_upload', 'Bulk Upload'),
    ]
    
    PLATFORM_CHOICES = [
        ('ios', 'iOS'),
        ('android', 'Android'),
        ('web', 'Web'),
    ]
    
    PROCESSING_STATUS_CHOICES = [
        ('uploaded', 'Uploaded'),
        ('processing', 'Processing'),
        ('processed', 'Processed'),
        ('failed', 'Failed'),
    ]
    
    MIME_TYPE_CHOICES = [
        ('image/jpeg', 'JPEG'),
        ('image/png', 'PNG'),
        ('image/heic', 'HEIC'),
        ('image/webp', 'WebP'),
    ]
    
    # 基本情報
    original_filename = models.CharField(max_length=255)
    server_filename = models.CharField(max_length=255, unique=True)
    file_url = models.URLField()
    file_size = models.BigIntegerField()
    mime_type = models.CharField(max_length=50, choices=MIME_TYPE_CHOICES)
    
    # 関連情報
    event_code = models.CharField(max_length=50, blank=True, null=True)
    team_name = models.CharField(max_length=255, blank=True, null=True)
    cp_number = models.IntegerField(blank=True, null=True)
    
    # アップロード情報
    upload_source = models.CharField(max_length=50, choices=UPLOAD_SOURCE_CHOICES, default='direct')
    device_platform = models.CharField(max_length=20, choices=PLATFORM_CHOICES, blank=True, null=True)
    
    # メタデータ
    capture_timestamp = models.DateTimeField(blank=True, null=True)
    upload_timestamp = models.DateTimeField(auto_now_add=True)
    device_info = models.TextField(blank=True, null=True)
    
    # 処理状況
    processing_status = models.CharField(max_length=20, choices=PROCESSING_STATUS_CHOICES, default='uploaded')
    thumbnail_url = models.URLField(blank=True, null=True)
    
    # 外部キー
    gpslog = models.ForeignKey('GpsCheckin', on_delete=models.SET_NULL, null=True, blank=True)
    entry = models.ForeignKey('Entry', on_delete=models.SET_NULL, null=True, blank=True)
    
    # システム情報
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    class Meta:
        db_table = 'rog_uploaded_images'
        indexes = [
            models.Index(fields=['event_code', 'team_name'], name='idx_uploaded_event_team'),
            models.Index(fields=['cp_number'], name='idx_uploaded_cp_number'),
            models.Index(fields=['upload_timestamp'], name='idx_uploaded_timestamp'),
            models.Index(fields=['processing_status'], name='idx_uploaded_status'),
        ]
        
    def __str__(self):
        return f"{self.original_filename} - {self.event_code} - CP{self.cp_number}"
    
    def clean(self):
        """バリデーション"""
        if self.file_size and (self.file_size <= 0 or self.file_size > 10485760):  # 10MB
            raise ValidationError("ファイルサイズは10MB以下である必要があります")
    
    @property
    def file_size_mb(self):
        """ファイルサイズをMB単位で取得"""
        return round(self.file_size / 1024 / 1024, 2) if self.file_size else 0


class NewEvent2(models.Model):
    # 既存フィールド
    event_name = models.CharField(max_length=255, unique=True)
    event_description=models.TextField(max_length=255,blank=True, null=True)
    start_datetime = models.DateTimeField(default=timezone.now)
    end_datetime = models.DateTimeField(null=True, blank=True)
    deadlineDateTime = models.DateTimeField(null=True, blank=True)

    #// Added @2024-10-21
    public = models.BooleanField(default=False)
    
    # Status field for enhanced event management (2025-08-27)
    STATUS_CHOICES = [
        ('public', 'Public'),
        ('private', 'Private'),
        ('draft', 'Draft'),
        ('closed', 'Closed'),
    ]
    status = models.CharField(
        max_length=20, 
        choices=STATUS_CHOICES, 
        default='draft',
        help_text="イベントステータス"
    )
    
    hour_3 = models.BooleanField(default=False)
    hour_5 = models.BooleanField(default=True)
    class_general = models.BooleanField(default=True)
    class_family = models.BooleanField(default=True)
    class_solo_male = models.BooleanField(default=True)
    class_solo_female = models.BooleanField(default=True)

    self_rogaining = models.BooleanField(default=False)
    
    # MobServer統合フィールド
    event_code = models.CharField(max_length=50, unique=True, blank=True, null=True)  # event_table.event_code
    start_time = models.CharField(max_length=20, blank=True, null=True)  # event_table.start_time
    event_day = models.CharField(max_length=20, blank=True, null=True)  # event_table.event_day
    
    # 会場情報統合
    venue_location = models.PointField(null=True, blank=True, srid=4326)
    venue_address = models.CharField(max_length=500, blank=True, null=True)

    def __str__(self):
        if self.event_code:
            return f"{self.event_code} - {self.event_name}"
        return f"{self.event_name} - From:{self.start_datetime} To:{self.end_datetime}"

    def save(self, *args, **kwargs):
        if not self.deadlineDateTime:
            self.deadlineDateTime = self.end_datetime #- timedelta(days=7)
        
        # publicフィールドからstatusフィールドへの自動移行
        if self.pk is None and self.status == 'draft':  # 新規作成時
            if self.public:
                self.status = 'public'
        
        super().save(*args, **kwargs)
    
    @property
    def deadline_datetime(self):
        """API応答用のフィールド名統一"""
        return self.deadlineDateTime
    
    def is_accessible_by_user(self, user):
        """ユーザーがこのイベントにアクセス可能かチェック"""
        if self.status == 'public':
            return True
        elif self.status == 'private':
            # スタッフ権限チェック（後で実装）
            return hasattr(user, 'staff_privileges') and user.staff_privileges
        elif self.status == 'draft':
            # ドラフトは管理者のみ
            return user.is_staff or user.is_superuser
        elif self.status == 'closed':
            return False
        return False

def get_default_end_datetime():
    """デフォルトの終了日時を取得"""
    from datetime import timedelta
    return timezone.now() + timedelta(days=1)

class NewEvent(models.Model):
    event_name = models.CharField(max_length=255, primary_key=True)
    start_datetime = models.DateTimeField(default=timezone.now)
    end_datetime = models.DateTimeField(default=get_default_end_datetime)

    def __str__(self):
        return f"{self.event_name} - From:{self.start_datetime} To:{self.end_datetime}"

def get_default_category():
    """デフォルトカテゴリーを取得または作成"""
    try:
        return NewCategory.objects.get_or_create(
            category_name="Default Category", 
            defaults={'category_number': 1}
        )[0].id
    except Exception:
        return 1  # フェイルセーフ

def get_default_multipoint():
    """デフォルトのMultiPointを取得"""
    try:
        from django.contrib.gis.geos import MultiPoint, Point
        return MultiPoint([Point(0, 0)])
    except Exception:
        return None

def get_default_event():
    """デフォルトイベントを取得または作成"""
    try:
        from datetime import timedelta
        return NewEvent2.objects.get_or_create(
            event_name="Default Event",
            defaults={
                'start_datetime': timezone.now(),
                'end_datetime': timezone.now() + timedelta(days=1)
            }
        )[0].id
    except Exception:
        return 1  # フェイルセーフ


class Team(models.Model):
    # 既存フィールド
    team_name = models.CharField(max_length=255)
    owner = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='owned_teams', blank=True, null=True)
    category = models.ForeignKey('NewCategory', on_delete=models.SET_DEFAULT, default=get_default_category)
    
    # MobServer統合フィールド
    zekken_number = models.CharField(max_length=20, blank=True, null=True)  # team_table.zekken_number
    event = models.ForeignKey('NewEvent2', on_delete=models.CASCADE, blank=True, null=True)  # team_table.event_code
    password = models.CharField(max_length=100, blank=True, null=True)  # team_table.password
    class_name = models.CharField(max_length=100, blank=True, null=True)  # team_table.class_name
    trial = models.BooleanField(default=False)  # team_table.trial
    
    # 地理情報
    location = models.PointField(null=True, blank=True, srid=4326)
    
    # 統合管理フィールド
    created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True)
    updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['zekken_number', 'event'],
                name='unique_team_per_event',
                condition=models.Q(zekken_number__isnull=False, event__isnull=False)
            )
        ]

    def __str__(self):
        if self.zekken_number and self.event:
            return f"{self.zekken_number}-{self.team_name} ({self.event.event_name})"
        return f"{self.team_name}, owner:{self.owner.lastname if self.owner else 'None'} {self.owner.firstname if self.owner else ''}"


class Member(models.Model):
    team = models.ForeignKey(Team, on_delete=models.CASCADE, related_name='members')
    user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
    firstname = models.CharField(max_length=255, blank=True, null=True)
    lastname = models.CharField(max_length=255, blank=True, null=True)
    date_of_birth = models.DateField(null=True, blank=True)
    female = models.BooleanField(default=False)

    is_temporary = models.BooleanField(default=False)   # Akira 2024-7-24

    class Meta:
        unique_together = ('team', 'user')

    def __str__(self):
        return f"{self.team.team_name} - {self.user.lastname} {self.user.firstname}"

#
class Category(models.Model):
    category_name = models.CharField(max_length=255, primary_key=True)
    category_number = models.IntegerField(default=0)
    duration = models.DurationField(default=timedelta(hours=5))
    num_of_member = models.IntegerField(default=1)
    family = models.BooleanField(default=False)
    female = models.BooleanField(default=False)

    class Meta:
        unique_together = ('category_name','category_number')

    def __str__(self):
        hours = self.duration.total_seconds() // 3600
        return f"{self.category_name} - {self.category_number} ({int(hours)}時間)"

    @property
    def hours(self):
        return self.duration.total_seconds() // 3600

class NewCategory(models.Model):
    category_name = models.CharField(max_length=255, unique=True)
    category_number = models.IntegerField(default=0)
    duration = models.DurationField(default=timedelta(hours=5))  
    num_of_member = models.IntegerField(default=1)
    family = models.BooleanField(default=False) 
    female = models.BooleanField(default=False) 
    trial = models.BooleanField(default=False) 

    class Meta:
        unique_together = ('category_name','category_number')

    def __str__(self):
        hours = self.duration.total_seconds() // 3600
        return f"{self.category_name} - {self.category_number} ({int(hours)}時間)"

    @property
    def hours(self):
        return self.duration.total_seconds() // 3600

class Entry(models.Model):
    team = models.ForeignKey(Team, on_delete=models.CASCADE)
    event = models.ForeignKey(NewEvent2, on_delete=models.CASCADE, default=get_default_event)
    category = models.ForeignKey(NewCategory, on_delete=models.CASCADE, default=get_default_category)
    date = models.DateTimeField(default=timezone.now)
    owner = models.ForeignKey(CustomUser, on_delete=models.CASCADE,blank=True, null=True)     # Akira 2024-7-24
    zekken_number = models.IntegerField(default=0)
    zekken_label = models.CharField(max_length=255, blank=True, null=True)
    is_active = models.BooleanField(default=True)  # 新しく追加
    hasParticipated = models.BooleanField(default=False)  # 新しく追加
    hasGoaled = models.BooleanField(default=False)  # 新しく追加
    
    # API変更要求書対応: スタッフ権限管理 (2025-08-27)
    staff_privileges = models.BooleanField(default=False, help_text="スタッフ権限フラグ")
    can_access_private_events = models.BooleanField(default=False, help_text="非公開イベント参加権限")
    
    VALIDATION_STATUS_CHOICES = [
        ('approved', 'Approved'),
        ('pending', 'Pending'),
        ('rejected', 'Rejected'),
    ]
    team_validation_status = models.CharField(
        max_length=20,
        choices=VALIDATION_STATUS_CHOICES,
        default='approved',
        help_text="チーム承認状況"
    )


    class Meta:
        unique_together = ('zekken_number', 'event','date')

    def __str__(self):
        return f"{self.zekken_number} - {self.team.team_name} - {self.event.event_name} - {self.date}"

    def clean(self):
        super().clean()
        if self.event and self.category and self.date:
            start = self.event.start_datetime
            end = self.event.end_datetime #- self.category.duration
            if not (start.date() <= self.date.date() <= end.date()):
                raise ValidationError({
                    'date': f'日時{self.date}は{start.date()}から{end.date()}の間である必要があります。'
                })
        
        # メンバーの年齢と性別をチェック
        if self.team:  # and not self.team.members.exists():
            members = self.team.members.all()  # membersを適切に取得
            if not members.exists():
                raise ValidationError({'team': 'チームにメンバーが登録されていません。'})
       
            #members = Member.objects.filter(team=self.team)
            #if not members.exists():
            #    # ここで、owner をMemberに登録する。  Akira 2024-7-24
            #    raise ValidationError("チームにメンバーが登録されていません。")
    
            adults = [m for m in members if self.is_adult(m.user.date_of_birth)]
            children = [m for m in members if self.is_child(m.user.date_of_birth)]
            teenagers = [m for m in members if self.is_teenager(m.user.date_of_birth)]
    
            if self.category.family:
                if not (adults and children):
                    raise ValidationError("ファミリーカテゴリーには、18歳以上のメンバーと小学生以下のメンバーが各1名以上必要です。")
            else:
                if not adults:
                    raise ValidationError("18歳以上のメンバーが1名以上必要です。")
                
                if children and not self.category.trial:
                    raise ValidationError("ファミリーカテゴリーまたはお試し以外では、小学生以下のメンバーは参加できません。")
    
            if self.category.num_of_member == 1:
                if len(members) != 1:
                    raise ValidationError("このカテゴリーはソロ参加のみ可能です。")
                if not adults:
                    raise ValidationError("ソロ参加は18歳以上のみ可能です。")
                if self.category.female and not members[0].user.female:
                    raise ValidationError("このカテゴリーは女性のみ参加可能です。")
                if not self.category.female and members[0].user.female:
                    raise ValidationError("このカテゴリーは男性のみ参加可能です。")
    
    
            if len(members) > self.category.num_of_member:
                raise ValidationError(f"このカテゴリーは{self.category.num_of_member}名までの参加が必要です。")


    def save(self, *args, **kwargs):
        self.full_clean()
        super().save(*args, **kwargs)

    @staticmethod
    def is_adult(birth_date):
        today = date.today()
        age = today.year - birth_date.year - ((today.month, today.day) < (birth_date.month, birth_date.day))
        return age >= 18

    @staticmethod
    def is_child(birth_date):
        today = date.today()
        age = today.year - birth_date.year - ((today.month, today.day) < (birth_date.month, birth_date.day))
        return age <= 12

    @staticmethod
    def is_teenager(birth_date):
        today = date.today()
        age = today.year - birth_date.year - ((today.month, today.day) < (birth_date.month, birth_date.day))
        return 13 <= age <= 17


class EntryMember(models.Model):
    entry = models.ForeignKey(Entry, on_delete=models.CASCADE)
    member = models.ForeignKey(Member, on_delete=models.CASCADE)
    is_temporary = models.BooleanField(default=False)   # Akira 2024-7-24

    class Meta:
        unique_together = ('entry', 'member')

    def __str__(self):
        return f"{self.entry.team.zekken_number} - {self.member.user.lastname} {self.member.user.firstname}"


class GoalImages(models.Model):
    user=models.ForeignKey(CustomUser, on_delete=models.DO_NOTHING)
    goalimage = models.FileField(upload_to='goals/%y%m%d', blank=True, null=True)
    goaltime = models.DateTimeField(_("Goal time"), blank=True, null=True,auto_now=False, auto_now_add=False)
    team_name = models.CharField(_("Team name"), max_length=255)
    event_code = models.CharField(_("event code"), max_length=255)
    cp_number = models.IntegerField(_("CP numner"))
    zekken_number = models.TextField(
        null=True,  # False にする
        blank=True, # False にする
        help_text="ゼッケン番号"
    )

class CheckinImages(models.Model):
    user=models.ForeignKey(CustomUser, on_delete=models.DO_NOTHING)
    checkinimage = models.FileField(upload_to='checkin/%y%m%d', blank=True, null=True)
    checkintime = models.DateTimeField(_("Goal time"), auto_now=False, auto_now_add=False)
    team_name = models.CharField(_("Team name"), max_length=255)
    event_code = models.CharField(_("event code"), max_length=255)
    cp_number = models.IntegerField(_("CP numner"))

class Checkpoint(models.Model):
    """チェックポイント管理モデル（MobServer統合）"""
    # MobServer完全統合
    cp_number = models.IntegerField()  # checkpoint_table.cp_number
    event = models.ForeignKey('NewEvent2', on_delete=models.CASCADE, blank=True, null=True)
    cp_name = models.CharField(max_length=200, blank=True, null=True)  # checkpoint_table.cp_name
    
    # 位置情報（PostGIS対応）
    location = models.PointField(srid=4326, blank=True, null=True)  # latitude, longitude統合
    
    # ポイント情報
    photo_point = models.IntegerField(default=0)  # checkpoint_table.photo_point
    buy_point = models.IntegerField(default=0)  # checkpoint_table.buy_point
    
    # サンプル・メモ
    sample_photo = models.CharField(max_length=500, blank=True, null=True)
    colabo_company_memo = models.TextField(blank=True, null=True)
    
    # 統合管理フィールド
    created_at = models.DateTimeField(auto_now_add=True, null=True, blank=True)
    updated_at = models.DateTimeField(auto_now=True, null=True, blank=True)
    
    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['cp_number', 'event'],
                name='unique_cp_per_event'
            )
        ]
        indexes = [
            models.Index(fields=['event', 'cp_number'], name='idx_checkpoint_event_cp'),
            GistIndex(fields=['location'], name='idx_checkpoint_location'),
        ]
    
    def __str__(self):
        return f"CP{self.cp_number} - {self.cp_name} ({self.event.event_code if self.event.event_code else self.event.event_name})"

class GpsCheckin(models.Model):
    id = models.AutoField(primary_key=True)
    event_code = models.CharField(max_length=255, null=False, default='')
    zekken = models.CharField(max_length=20, null=True, blank=True)
    serial_number = models.CharField(max_length=20, null=True, blank=True)
    cp_number = models.CharField(max_length=20, null=True, blank=True)
    lat = models.FloatField(null=True, blank=True)
    lng = models.FloatField(null=True, blank=True)
    checkin_time = models.DateTimeField(null=True, blank=True)
    record_time = models.DateTimeField(null=True, blank=True)
    location = models.PointField(srid=4326, null=True, blank=True)
    mobserver_id = models.IntegerField(null=True, blank=True)
    event_id = models.BigIntegerField(null=True, blank=True)
    team_id = models.BigIntegerField(null=True, blank=True)
    checkpoint_id = models.BigIntegerField(null=True, blank=True)

    class Meta:
        db_table = 'rog_gpscheckin'
        managed = True

    def __str__(self):
        return f"GPS Checkin {self.id} - {self.zekken}"

    def save(self, *args, **kwargs):
        # 作成時・更新時のタイムスタンプを自動設定
        from django.utils import timezone
        if not self.create_at:
            self.create_at = timezone.now()
        self.update_at = timezone.now()
        super().save(*args, **kwargs)

class RogUser(models.Model):
    user=models.OneToOneField(CustomUser, on_delete=models.CASCADE)
    phone=models.CharField(_('Phone Number'), max_length=55)
    first_name=models.CharField(_('First Name'), max_length=255)
    middle_name=models.CharField(_('Middle Name'), max_length=255, blank=True, null=True)
    last_name=models.CharField(_('last_name'), max_length=255)
    nickname=models.CharField(_('Nickname'), max_length=255, blank=True, null=True)
    country=models.CharField(_('Country'), max_length=255, default='Japan')
    language=models.CharField(_('Language'), max_length=255, default='Japanese')
    prefecture=models.CharField(_('Prefecture'), max_length=255, blank=True, null=True)
    sex=models.CharField(_('Sex'), max_length=255, default='unknown', blank=True, null=True)
    birthyear=models.IntegerField(_('Birth year'), blank=True, null=True)
    family_structure =models.IntegerField(_('Family Structure'), blank=True, null=True)
    introducer = models.ForeignKey(CustomUser, related_name='introduced_uesr', on_delete=models.DO_NOTHING)
    level= models.IntegerField(_('Level'), blank=True, null=True, default=0)
    paid=models.BooleanField(_("Is Paid"),default=False)
    parammeters=models.CharField(_('Parameters'), max_length=512, blank=True, null=True)
    created_at=models.DateTimeField(auto_now_add=True)
    last_updated_user=models.ForeignKey(CustomUser, related_name="roguser_updated_user", on_delete=models.DO_NOTHING)
    last_updated_at=models.DateTimeField(auto_now=True)



class SystemSettings(models.Model):
    setting_name=models.CharField(_('Settings Name'), max_length=255)
    version=models.CharField(_('Version'), max_length=10, blank=True, null=True)
    effective_date=models.DateTimeField()
    end_date=models.DateTimeField()
    parammeters=models.CharField(_('Parameters'), max_length=512)
    created_at=models.DateTimeField(auto_now_add=True)
    last_updated_user=models.ForeignKey(CustomUser, related_name="system_setting_updated_user", on_delete=models.DO_NOTHING)
    last_updated_at=models.DateTimeField(auto_now=True)

class Location(models.Model):
    location_id=models.IntegerField(_('Location id'), blank=True, null=True, db_index=True)
    sub_loc_id=models.CharField(_('Sub location id'), max_length=2048, blank=True, null=True)
    cp=models.FloatField(_('Check Point'), blank=False, null=True, default=0)
    location_name=models.CharField(_('Location Name'), max_length=2048, default="--- 場所をお願いします --")
    category=models.CharField(_('Category'), max_length=2048, blank=True, null=True, db_index=True)
    subcategory=models.CharField(_('Sub Category'), max_length=2048, blank=True, null=True)
    zip=models.CharField(_('Zip code'), max_length=12, blank=True, null=True)
    address = models.CharField(_('Address'), max_length=2048, blank=True, null=True)
    prefecture = models.CharField(_('Prefecture'), max_length=2048, blank=True, null=True)
    area= models.CharField(_('Area'), max_length=2048, blank=True, null=True)
    city= models.CharField(_('City'), max_length=2048, blank=True, null=True)
    latitude = models.FloatField('Latitude', blank=True, null=True)
    longitude = models.FloatField('Latitude', blank=True, null=True)
    photos=models.CharField(_('Photos'), max_length=2048, blank=True, null=True)
    videos=models.CharField(_('Videos'), max_length=2048, blank=True, null=True)
    webcontents=models.CharField(_('Web Content'), max_length=2048, blank=True, null=True)
    status=models.CharField(_('Status'),max_length=2048, blank=True, null=True)
    portal=models.CharField(_('Portal'), max_length=2048,blank=True, null=True)
    group=models.CharField(_('Group'), max_length=2048,blank=True, null=True, db_index=True)
    phone=models.CharField(_('Phone'), max_length=2048,blank=True, null=True)
    fax=models.CharField(_('Fax'), max_length=2048, blank=True, null=True)
    email=models.EmailField(_('Email'), max_length=2048,blank=True, null=True)
    facility=models.CharField(_('Facility'), max_length=2048, blank=True, null=True)
    remark=models.CharField(_('Remarks'), max_length=2048, blank=True, null=True)
    tags=models.CharField(_('Tags'), max_length=2048, blank=True, null=True)
    event_name = models.CharField(_('Event name'), max_length=2048, blank=True, null=True, db_index=True)
    event_active = models.BooleanField(_("Is Event active"),default=True, db_index=True)
    hidden_location = models.BooleanField(_("Is Hidden Location"),default=False)
    auto_checkin = models.BooleanField(_("Is AutoCheckin"),default=False)
    checkin_radius = models.FloatField(_("Checkin radious"), blank=True, null=True, default=15.0)
    checkin_point = models.FloatField(_("Checkin Point"), blank=True, null=True, default=10)
    buy_point = models.FloatField(_("buy Point"), blank=True, null=True, default=0)
    evaluation_value = models.CharField(_('Evaluation value (評価)'),max_length=2048, blank=True, null=True)
    shop_closed = models.BooleanField(_("Shop Closed (休業)"),default=False)
    shop_shutdown = models.BooleanField(_("Shop Shutdown (閉業)"),default=False)
    opening_hours_mon = models.CharField(_("Opening hours monday (月曜)"),max_length=512, blank=True, null=True)
    opening_hours_tue = models.CharField(_("Opening hours tuesday (火曜)"), max_length=512,blank=True, null=True)
    opening_hours_wed = models.CharField(_("Opening hours wednesday (水曜)"), max_length=512,blank=True, null=True)
    opening_hours_thu = models.CharField(_("Opening hours thursday (木曜)"), max_length=512, blank=True, null=True)
    opening_hours_fri = models.CharField(_("Opening hours frinday (金曜)"), max_length=512,blank=True, null=True)
    opening_hours_sat = models.CharField(_("Opening hours saturday (土曜)"), max_length=512,blank=True, null=True)
    opening_hours_sun = models.CharField(_("Opening hours sunday (日曜)"),max_length=512, blank=True, null=True)
    parammeters=models.CharField(_('Parameters'), max_length=2048, blank=True, null=True)
    created_at=models.DateTimeField(auto_now_add=True)
    last_updated_user=models.ForeignKey(CustomUser, related_name="location_updated_user", on_delete=models.DO_NOTHING,blank=True, null=True)
    last_updated_at=models.DateTimeField(auto_now=True)
    geom=models.MultiPointField(srid=4326, default=get_default_multipoint)

    class Meta:
        indexes = [
            GistIndex(fields=['geom']),
        ]

    def __str__(self):
        return self.location_name


class templocation(models.Model):
    location_id=models.IntegerField(_('Location id'), blank=True, null=True)
    sub_loc_id=models.CharField(_('Sub location id'), max_length=2048, blank=True, null=True)
    cp=models.FloatField(_('Check Point'), blank=False, null=True, default=0)
    location_name=models.CharField(_('Location Name'), max_length=2048, default="--- 場所をお願いします --")
    category=models.CharField(_('Category'), max_length=2048, blank=True, null=True)
    subcategory=models.CharField(_('Sub Category'), max_length=2048, blank=True, null=True)
    zip=models.CharField(_('Zip code'), max_length=12, blank=True, null=True)
    address = models.CharField(_('Address'), max_length=2048, blank=True, null=True)
    prefecture = models.CharField(_('Prefecture'), max_length=2048, blank=True, null=True)
    area= models.CharField(_('Area'), max_length=2048, blank=True, null=True)
    city= models.CharField(_('City'), max_length=2048, blank=True, null=True)
    latitude = models.FloatField('Latitude', blank=True, null=True)
    longitude = models.FloatField('Latitude', blank=True, null=True)
    photos=models.CharField(_('Photos'), max_length=2048, blank=True, null=True)
    videos=models.CharField(_('Videos'), max_length=2048, blank=True, null=True)
    webcontents=models.CharField(_('Web Content'), max_length=2048, blank=True, null=True)
    status=models.CharField(_('Status'),max_length=2048, blank=True, null=True)
    portal=models.CharField(_('Portal'), max_length=2048,blank=True, null=True)
    group=models.CharField(_('Group'), max_length=2048,blank=True, null=True)
    phone=models.CharField(_('Phone'), max_length=2048,blank=True, null=True)
    fax=models.CharField(_('Fax'), max_length=2048, blank=True, null=True)
    email=models.EmailField(_('Email'), max_length=2048,blank=True, null=True)
    facility=models.CharField(_('Facility'), max_length=2048, blank=True, null=True)
    remark=models.CharField(_('Remarks'), max_length=2048, blank=True, null=True)
    tags=models.CharField(_('Tags'), max_length=2048, blank=True, null=True)
    event_name = models.CharField(_('Event name'), max_length=2048, blank=True, null=True)
    event_active = models.BooleanField(_("Is Event active"),default=True)
    hidden_location = models.BooleanField(_("Is Hidden Location"),default=False)
    auto_checkin = models.BooleanField(_("Is AutoCheckin"),default=False)
    checkin_radius = models.FloatField(_("Checkin radious"), blank=True, null=True, default=15.0)
    checkin_point = models.FloatField(_("Checkin Point"), blank=True, null=True, default=10)
    buy_point = models.FloatField(_("buy Point"), blank=True, null=True, default=0)
    evaluation_value = models.CharField(_('Evaluation value (評価)'),max_length=2048, blank=True, null=True)
    shop_closed = models.BooleanField(_("Shop Closed (休業)"),default=False)
    shop_shutdown = models.BooleanField(_("Shop Shutdown (閉業)"),default=False)
    opening_hours_mon = models.CharField(_("Opening hours monday (月曜)"),max_length=512, blank=True, null=True)
    opening_hours_tue = models.CharField(_("Opening hours tuesday (火曜)"), max_length=512,blank=True, null=True)
    opening_hours_wed = models.CharField(_("Opening hours wednesday (水曜)"), max_length=512,blank=True, null=True)
    opening_hours_thu = models.CharField(_("Opening hours thursday (木曜)"), max_length=512, blank=True, null=True)
    opening_hours_fri = models.CharField(_("Opening hours frinday (金曜)"), max_length=512,blank=True, null=True)
    opening_hours_sat = models.CharField(_("Opening hours saturday (土曜)"), max_length=512,blank=True, null=True)
    opening_hours_sun = models.CharField(_("Opening hours sunday (日曜)"),max_length=512, blank=True, null=True)
    parammeters=models.CharField(_('Parameters'), max_length=2048, blank=True, null=True)
    created_at=models.DateTimeField(auto_now_add=True)
    last_updated_user=models.ForeignKey(CustomUser, related_name="temp_location_updated_user", on_delete=models.DO_NOTHING,blank=True, null=True)
    last_updated_at=models.DateTimeField(auto_now=True)
    geom=models.MultiPointField(srid=4326)

    def __str__(self):
        return self.location_name


class Location2025(models.Model):
    """
    2025年版チェックポイント管理モデル
    CSVアップロード対応の新しいチェックポイント管理システム
    """
    # 基本情報
    cp_number = models.IntegerField(_('CP番号'), db_index=True)
    event = models.ForeignKey('NewEvent2', on_delete=models.CASCADE, verbose_name=_('イベント'))
    cp_name = models.CharField(_('CP名'), max_length=255)
    
    # 位置情報
    latitude = models.FloatField(_('緯度'), null=True, blank=True)
    longitude = models.FloatField(_('経度'), null=True, blank=True)
    location = models.PointField(_('位置'), srid=4326, null=True, blank=True)
    
    # ポイント情報
    cp_point = models.IntegerField(_('チェックポイント得点'), default=10)
    photo_point = models.IntegerField(_('写真ポイント'), default=0)
    buy_point = models.IntegerField(_('買い物ポイント'), default=0)
    
    # チェックイン設定
    checkin_radius = models.FloatField(_('チェックイン範囲(m)'), default=15.0)
    auto_checkin = models.BooleanField(_('自動チェックイン'), default=False)
    
    # 営業情報
    shop_closed = models.BooleanField(_('休業中'), default=False)
    shop_shutdown = models.BooleanField(_('閉業'), default=False)
    opening_hours = models.TextField(_('営業時間'), blank=True, null=True)
    
    # 詳細情報
    address = models.CharField(_('住所'), max_length=512, blank=True, null=True)
    phone = models.CharField(_('電話番号'), max_length=32, blank=True, null=True)
    website = models.URLField(_('ウェブサイト'), blank=True, null=True)
    description = models.TextField(_('説明'), blank=True, null=True)
    
    # 管理情報
    is_active = models.BooleanField(_('有効'), default=True, db_index=True)
    sort_order = models.IntegerField(_('表示順'), default=0)
    
    # CSVアップロード関連
    csv_source_file = models.CharField(_('CSVファイル名'), max_length=255, blank=True, null=True)
    csv_upload_date = models.DateTimeField(_('CSVアップロード日時'), null=True, blank=True)
    csv_upload_user = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True, blank=True, 
                                       related_name='location2025_csv_uploads', verbose_name=_('CSVアップロードユーザー'))
    
    # タイムスタンプ
    created_at = models.DateTimeField(_('作成日時'), auto_now_add=True)
    updated_at = models.DateTimeField(_('更新日時'), auto_now=True)
    created_by = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True, blank=True,
                                  related_name='location2025_created', verbose_name=_('作成者'))
    updated_by = models.ForeignKey(CustomUser, on_delete=models.SET_NULL, null=True, blank=True,
                                  related_name='location2025_updated', verbose_name=_('更新者'))

    class Meta:
        db_table = 'rog_location2025'
        verbose_name = _('チェックポイント2025')
        verbose_name_plural = _('チェックポイント2025')
        unique_together = ['cp_number', 'event']
        ordering = ['event', 'sort_order', 'cp_number']
        indexes = [
            models.Index(fields=['event', 'cp_number'], name='location2025_event_cp_idx'),
            models.Index(fields=['event', 'is_active'], name='location2025_event_active_idx'),
            models.Index(fields=['csv_upload_date'], name='location2025_csv_date_idx'),
            GistIndex(fields=['location'], name='location2025_location_gist_idx'),
        ]

    def save(self, *args, **kwargs):
        # 緯度経度からLocationフィールドを自動生成
        if self.latitude and self.longitude:
            from django.contrib.gis.geos import Point
            self.location = Point(self.longitude, self.latitude, srid=4326)
        super().save(*args, **kwargs)

    def __str__(self):
        return f"{self.event.event_name} - CP{self.cp_number}: {self.cp_name}"

    @property
    def total_point(self):
        """総得点を計算"""
        return self.cp_point + self.photo_point + self.buy_point

    @classmethod
    def import_from_csv(cls, csv_file, event, user=None):
        """
        CSVファイルからチェックポイントデータをインポート
        
        CSV形式:
        cp_number,cp_name,latitude,longitude,cp_point,photo_point,buy_point,address,phone,description
        """
        import csv
        import io
        from django.utils import timezone
        
        if isinstance(csv_file, str):
            # ファイルパスの場合
            with open(csv_file, 'r', encoding='utf-8') as f:
                csv_content = f.read()
        else:
            # アップロードされたファイルの場合
            csv_content = csv_file.read().decode('utf-8')
        
        csv_reader = csv.DictReader(io.StringIO(csv_content))
        created_count = 0
        updated_count = 0
        errors = []
        
        for row_num, row in enumerate(csv_reader, start=2):
            try:
                cp_number = int(row.get('cp_number', 0))
                if cp_number <= 0:
                    errors.append(f"行{row_num}: CP番号が無効です")
                    continue
                
                defaults = {
                    'cp_name': row.get('cp_name', f'CP{cp_number}'),
                    'latitude': float(row['latitude']) if row.get('latitude') else None,
                    'longitude': float(row['longitude']) if row.get('longitude') else None,
                    'cp_point': int(row.get('cp_point', 10)),
                    'photo_point': int(row.get('photo_point', 0)),
                    'buy_point': int(row.get('buy_point', 0)),
                    'address': row.get('address', ''),
                    'phone': row.get('phone', ''),
                    'description': row.get('description', ''),
                    'csv_source_file': getattr(csv_file, 'name', 'uploaded_file.csv'),
                    'csv_upload_date': timezone.now(),
                    'csv_upload_user': user,
                    'updated_by': user,
                }
                
                if user:
                    defaults['created_by'] = user
                
                obj, created = cls.objects.update_or_create(
                    cp_number=cp_number,
                    event=event,
                    defaults=defaults
                )
                
                if created:
                    created_count += 1
                else:
                    updated_count += 1
                    
            except (ValueError, KeyError) as e:
                errors.append(f"行{row_num}: {str(e)}")
                continue
        
        return {
            'created': created_count,
            'updated': updated_count,
            'errors': errors
        }


class Location_line(models.Model):
    location_id=models.IntegerField(_('Location id'), blank=True, null=True)
    location_name=models.CharField(_('Location Name'), max_length=255)
    category=models.CharField(_('Category'), max_length=255, blank=True, null=True)
    zip=models.CharField(_('Zip code'), max_length=12, blank=True, null=True)
    address = models.CharField(_('Address'), max_length=512, blank=True, null=True)
    prefecture = models.CharField(_('Prefecture'), max_length=255, blank=True, null=True)
    area= models.CharField(_('Area'), max_length=255, blank=True, null=True)
    city= models.CharField(_('City'), max_length=255, blank=True, null=True)
    photos=models.CharField(_('Phptos'), max_length=255, blank=True, null=True)
    videos=models.CharField(_('Videos'), max_length=255, blank=True, null=True)
    webcontents=models.CharField(_('Web Content'), max_length=255, blank=True, null=True)
    status=models.CharField(_('Status'),max_length=255, blank=True, null=True)
    portal=models.CharField(_('Portal'), max_length=255,blank=True, null=True)
    group=models.CharField(_('Group'), max_length=255,blank=True, null=True)
    phone=models.CharField(_('Phone'), max_length=255,blank=True, null=True)
    fax=models.CharField(_('Fax'), max_length=255, blank=True, null=True)
    email=models.EmailField(_('Email'), max_length=255,blank=True, null=True)
    facility=models.CharField(_('Facility'), max_length=255, blank=True, null=True)
    remark=models.CharField(_('Remarks'), max_length=255, blank=True, null=True)
    tags=models.CharField(_('Tags'), max_length=512, blank=True, null=True)
    parammeters=models.CharField(_('Parameters'), max_length=512, blank=True, null=True)
    created_at=models.DateTimeField(auto_now_add=True)
    last_updated_user=models.ForeignKey(CustomUser, related_name="location_line_updated_user", on_delete=models.DO_NOTHING,blank=True, null=True)
    last_updated_at=models.DateTimeField(auto_now=True)
    geom=models.MultiLineStringField(srid=4326, blank=True, null=True)
    
    def __str__(self):
        return str(self.location_id)



class Location_polygon(models.Model):
    location_id=models.IntegerField(_('Location id'), blank=True, null=True)
    location_name=models.CharField(_('Location Name'), max_length=255)
    category=models.CharField(_('Category'), max_length=255, blank=True, null=True)
    zip=models.CharField(_('Zip code'), max_length=12, blank=True, null=True)
    address = models.CharField(_('Address'), max_length=512, blank=True, null=True)
    prefecture = models.CharField(_('Prefecture'), max_length=255, blank=True, null=True)
    area= models.CharField(_('Area'), max_length=255, blank=True, null=True)
    city= models.CharField(_('City'), max_length=255, blank=True, null=True)
    photos=models.CharField(_('Phptos'), max_length=255, blank=True, null=True)
    videos=models.CharField(_('Videos'), max_length=255, blank=True, null=True)
    webcontents=models.CharField(_('Web Content'), max_length=255, blank=True, null=True)
    status=models.CharField(_('Status'),max_length=255, blank=True, null=True)
    portal=models.CharField(_('Portal'), max_length=255,blank=True, null=True)
    group=models.CharField(_('Group'), max_length=255,blank=True, null=True)
    phone=models.CharField(_('Phone'), max_length=255,blank=True, null=True)
    fax=models.CharField(_('Fax'), max_length=255, blank=True, null=True)
    email=models.EmailField(_('Email'), max_length=255,blank=True, null=True)
    facility=models.CharField(_('Facility'), max_length=255, blank=True, null=True)
    remark=models.CharField(_('Remarks'), max_length=255, blank=True, null=True)
    tags=models.CharField(_('Tags'), max_length=512, blank=True, null=True)
    parammeters=models.CharField(_('Parameters'), max_length=512, blank=True, null=True)
    created_at=models.DateTimeField(auto_now_add=True)
    last_updated_user=models.ForeignKey(CustomUser, related_name="location_polygon_updated_user", on_delete=models.DO_NOTHING,blank=True, null=True)
    last_updated_at=models.DateTimeField(auto_now=True)
    geom=models.MultiPolygonField(srid=4326, blank=True, null=True)

    def __str__(self):
        return str(self.location_name)


class UserTracks(models.Model):
    user=models.ForeignKey(CustomUser, on_delete=models.DO_NOTHING)
    geom=models.MultiPointField(srid=4326)
    created_at=models.DateTimeField(auto_now_add=True)



EVENT_STATUS = (
    ("PREPARING", _("Preparing")),
    ("PROMOTION", _("Promotion")),
    ("EVENT", _("Event")),
    ("END", _("End"))
)
    

class Event(models.Model):
    user=models.ManyToManyField(CustomUser, through='EventUser', related_name='even')
    tagname=models.CharField(_('Parameters'), max_length=512, blank=True, null=True)
    status=models.CharField(max_length=256, choices=EVENT_STATUS)
    price=models.IntegerField(_('Paid Amount'), default=0)
    promotion_date=models.DateTimeField(_('Promotion date'), blank=True, null=True)
    event_start=models.DateTimeField(_('Promotion date'), blank=True, null=True)
    event_end=models.DateTimeField(_('Promotion date'), blank=True, null=True)
    remark=models.CharField(max_length=256, blank=True, null=True)
    parammeters=models.CharField(_('Parameters'), max_length=512, blank=True, null=True)
    created_at=models.DateTimeField(auto_now_add=True)
    last_updated_user=models.ForeignKey(CustomUser, related_name="event_updated_user", on_delete=models.DO_NOTHING,blank=True, null=True)
    last_updated_at=models.DateTimeField(auto_now=True)


class EventUser(models.Model):
    user=models.ForeignKey(CustomUser,on_delete=models.CASCADE, related_name='user')
    event=models.ForeignKey(Event, on_delete=models.CASCADE, related_name='event')


ROG_STATUS = (
    ("REGISTERED", _("Registered")),
    ("ACCEPTED", _("accepted")),
    ("PAID", _("paid")),
    ("JOINED", _("joined")),
    ("CANCELED", _("Canceled"))
)
    
class JoinedEvent(models.Model):
    user=models.ForeignKey(CustomUser, on_delete=models.DO_NOTHING)
    tagname=models.CharField(_('Tag Name'), max_length=255, blank=True, null=True)
    status=models.CharField(max_length=256, choices=ROG_STATUS)
    registrationid=models.CharField(_('Registration Id'), max_length=56)
    payment_code=models.CharField(_('Payment Code'), max_length=255)
    paid=models.IntegerField(_('Paid Amount'), default=0)
    remark=models.CharField(_('Remark'), max_length=255, blank=True, null=True)
    parammeters=models.CharField(_('Parameters'), max_length=512)
    created_at=models.DateTimeField(auto_now_add=True)
    last_updated_user=models.ForeignKey(CustomUser, related_name="joined_event_updated_user", on_delete=models.DO_NOTHING)
    last_updated_at=models.DateTimeField(auto_now=True)



class Favorite(models.Model):
    user=models.ForeignKey(CustomUser, on_delete=models.DO_NOTHING)
    location=models.ForeignKey(Location, on_delete=models.CASCADE)
    good=models.IntegerField(_('Good'), default=0)
    favorite=models.IntegerField(_('Favorite'), default=0)
    evaluation=models.IntegerField(_('Evaluation'), default=0)
    number_visit=models.IntegerField(_('Good'), default=0)
    last_visited=models.DateTimeField(_('Last Visited'), blank=True, null=True)
    parammeters=models.CharField(_('Parameters'), max_length=512, blank=True, null=True)
    created_at=models.DateTimeField(auto_now_add=True)
    last_updated_user=models.ForeignKey(CustomUser, related_name="favorite_updated_user", on_delete=models.DO_NOTHING)
    last_updated_at=models.DateTimeField(auto_now=True)



TRAVEL_CATEGORY = (
    ("PRIVATE", _("Private")),
    ("GROUP", _("Group")),
    ("AGENT", _("Agent")),
    ("ROGAINING", _("Rogaining"))
)



class TravelList(models.Model):
    travel_id=  models.IntegerField(_('Travel Id'))
    user=models.ForeignKey(CustomUser, on_delete=models.DO_NOTHING)
    start_date=models.DateTimeField(_('Start date') ,blank=True, null=True)
    finish_date=models.DateTimeField(_('End date') ,blank=True, null=True)
    category=models.CharField(max_length=256, choices=TRAVEL_CATEGORY)
    title=models.CharField(_('Title'), max_length=255)
    transportation=models.CharField(_('Transpotation'), max_length=255 ,blank=True, null=True)
    moving_distance=models.IntegerField(blank=True, null=True)
    duration=models.DurationField(_('Duration') ,blank=True, null=True)
    eta=models.DateTimeField(blank=True, null=True)
    parammeters=models.CharField(_('Parameters'), max_length=512 ,blank=True, null=True)
    created_at=models.DateTimeField(auto_now_add=True)
    last_updated_user=models.ForeignKey(CustomUser, related_name="travel_list_updated_user", on_delete=models.DO_NOTHING)
    last_updated_at=models.DateTimeField(auto_now=True)


class TravelPoint(models.Model):
    travel_list=  models.ForeignKey(TravelList, on_delete=models.DO_NOTHING)
    location=models.ForeignKey(Location, on_delete=models.CASCADE)
    distance=models.FloatField(blank=True, null=True)
    transportation=models.CharField(_('Transpotation'), max_length=255 ,blank=True, null=True)
    eta=models.DateTimeField(blank=True, null=True)
    order_number=models.IntegerField(blank=True, null=True)
    parammeters=models.CharField(_('Parameters'), max_length=512 ,blank=True, null=True)
    created_at=models.DateTimeField(auto_now_add=True)
    last_updated_user=models.ForeignKey(CustomUser, related_name="travelpoint_updated_user", on_delete=models.DO_NOTHING)
    last_updated_at=models.DateTimeField(auto_now=True)


class Useractions(models.Model):
    user=models.ForeignKey(CustomUser, related_name="action_user", on_delete=models.CASCADE)
    location=models.ForeignKey(Location, related_name="action_location", on_delete=models.CASCADE)
    wanttogo=models.BooleanField(default=False)
    like=models.BooleanField(default=False)
    checkin=models.BooleanField(default=False)
    checkinimage=models.FileField(upload_to='%y%m%d', blank=True, null=True)
    order =models.IntegerField(default=0)
    created_at=models.DateTimeField(auto_now_add=True)
    last_updated_at=models.DateTimeField(auto_now=True)


class TestModel(models.Model):
    testbane=models.CharField(_("test field"), max_length=355)
    wanttogo=models.BooleanField(default=False)
    like=models.BooleanField(default=False)
    checkin=models.BooleanField(default=False)
    created_at=models.DateTimeField(auto_now_add=True)
    last_updated_at=models.DateTimeField(auto_now=True)


def getTableForModel(tbl):
    if tbl == 1:
        return templocation.objects.model._meta.db_table;
    elif tbl == 2:
        return Location_line.objects.model._meta.db_table;
    else:
        return Location_polygon.objects.model._meta.db_table;

def getTempMappingforModel(tbl, shp):
    if tbl == 1:
        return LayerMapping(templocation, shp, location_mapping, transform=False)
    elif tbl == 2:
        return LayerMapping(Location_line, shp, location_line_mapping, transform=False)
    else:
        return LayerMapping(Location_polygon, shp, location_polygon_mapping, transform=False) 


class ShapeLayers(models.Model):
    name = models.CharField(_("Shape Layer"), max_length=255)
    file = models.FileField(upload_to=get_file_path, blank=True)
    uploaded_date = models.DateField(auto_now_add=True)
    layerof = models.IntegerField(choices=LAYER_CHOICES, default=1)
    table_name = models.CharField(_("Table name"), max_length=255, blank=True)

    def __str__(self):
        return self.name

class ShapeFileLocations(models.Model):
    shapefile=models.CharField(_('Shapelayer'), max_length=2048 ,blank=True, null=True)
    locid=models.IntegerField(blank=True, null=True)


@receiver(pre_save, sender=Location)
def location_presave(sender, instance, *args, **kwargs):
    print("------############------------", instance.location_id)
    templocation.objects.filter(location_id = instance.location_id).delete()


@receiver(pre_save, sender=Location_line)
def location_presave(sender, instance, *args, **kwargs):
    Location_line.objects.filter(location_id = instance.location_id).delete()

@receiver(pre_save, sender=Location_polygon)
def location_presave(sender, instance, *args, **kwargs):
    Location_polygon.objects.filter(location_id = instance.location_id).delete()



@receiver(pre_save, sender=ShapeLayers)
def my_callback(sender, instance, *args, **kwargs):

    instance.table_name = getTableForModel(instance.layerof)
    

def deletePrevious(mdl, fields):
    with transaction.atomic():
        mdl.objects.filter(location_id = int(fields[0])).delete();


@receiver(post_delete,sender=ShapeLayers)
def deleteShapelocation(sender,instance,*args,**kwargs):
    locids = ShapeFileLocations.objects.filter(shapefile=instance.name).values_list('locid')
    print("------- name----")
    print(locids)
    print("------- name----")
    templocation.objects.all().delete()
    ShapeFileLocations.objects.filter(shapefile=instance.name).delete()

    

@receiver(post_save, sender=ShapeLayers)
def publish_data(sender, instance, created, **kwargs):
    logger.info(f"Processing ShapeLayer: {instance.name}")

    file = instance.file.path
    file_format = os.path.basename(file).split('.')[-1]
    file_name = os.path.basename(file).split('.')[0]
    file_path = os.path.dirname(file)
    name = instance.name
    conn_str = f'postgresql://{env("POSTGRES_USER")}:{env("POSTGRES_PASS")}@{env("PG_HOST")}:{env("PG_PORT")}/{env("POSTGRES_DBNAME")}'

    with zipfile.ZipFile(file, 'r') as zip_ref:
        zip_ref.extractall(file_path)
    
    os.remove(file)

    try:
        #logger.debug("Attempting to read shape file")

        # print("before reading the file")
        shp = glob.glob(r'{}/**/*.shp'.format(file_path), recursive=True)[0]

        #logger.info(f"Shape file read: {shp}")

        # print("this is the read file",shp)
        gdf = gpd.read_file(shp)
        crs_name = str(gdf.crs.srs)
        #logger.debug(f"CRS name: {crs_name}")

        # print(crs_name, 'crs - name')
        epsg = int(crs_name.replace('epsg:',''))
        if epsg is None:
            epsg=4326

        lm2 = getTempMappingforModel(instance.layerof, shp)
        #logger.info("Saving to temporary table")

        # print("### shape file is ###")
        lm2.save(strict=True, verbose=True)
        #logger.info("Save to temporary table completed")

        os.remove(shp)
    except Exception as e:
        print('######## shape file##########',e)

    try:
        logger.debug("Attempting to read CSV file")

        csv_f = glob.glob(r'{}/**/*.csv'.format(file_path), recursive=True)[0]

        remove_bom_inplace(csv_f)
        
        mdl = apps.get_model(app_label="rog", model_name=LAYER_CHOICES[instance.layerof -1][1])
        print(mdl)
        print(f"#### instance.layerof - {instance.layerof}")
        #logger.debug(f"Model for layer: {mdl}")

        with open(csv_f, mode="r", encoding="utf-8") as txt_file:
            #heading = next(txt_file)
            reader = csv.reader(txt_file, delimiter=",")
            for fields in reader:
                logger.debug(f"Processing row: {fields[0]}")
                print("@@@@@@@@@@@@")
                print(fields[0])
                print("@@@@@@@@@@@@")
                if instance.layerof == 1:
                    #insertShapeLayerLocation(instance.name, fields)
                    #updateLocation(mdl, fields)
                    update_or_create_location(mdl, fields)
                if instance.layerof == 2:
                    updateLineTable(mdl, fields)
                if instance.layerof == 3:
                    updatePolygonTable(mdl, fields)

        with open(csv_f, mode="r", encoding="utf-8") as txt_file:
            reader_2 = csv.reader(txt_file, delimiter=",")
            for fields in reader_2:
                logger.debug(f"Inserting ShapeLayerLocation: {fields[0]}")
                print("@@@@@@@@@@@@")
                print(fields[0])
                print("@@@@@@@@@@@@")
                if instance.layerof == 1:
                    insertShapeLayerLocation(instance.name, fields)

        logger.info("CSV processing completed")
    
    except Exception as e:
        print('######## csv file ##########',e)


def insertShapeLayerLocation(name, fields):
    logger.info(f"Attempting to insert ShapeFileLocations for file: {name}, location_id: {fields[0]}")
    try:
        sll = UserUploadUser(userfile=name, email=fields[0])
        sll.save();
    except Exception as e:
        logger.error(f"Error inserting ShapeFileLocations: {e}", exc_info=True)


def insertUserUploadUser(name, fields):
    try:
        with transaction.atomic():
            sll = UserUploadUser(userfile=name, email=fields[0])
            sll.save()
    except Exception as e:
        logger.error(f"Error updating TempLocation: {e}", exc_info=True)

def update_or_create_location(mdl, fields):
    try:
        with transaction.atomic():
            latitude = float(fields[11]) if fields[11] and len(fields[11]) > 0 else None
            longitude = float(fields[12]) if fields[12] and len(fields[12]) > 0 else None

            geom = MultiPoint(Point(longitude, latitude)) if latitude is not None and longitude is not None else None
        

            defaults={
                'sub_loc_id': fields[1] if len(fields[1]) > 0 else '',
                'cp': fields[2] if len(fields[2]) > 0 else 0,
                # その他のフィールド...
                'location_name': fields[3] if len(fields[3]) > 0 else '',
                'category': fields[4] if len(fields[4]) > 0 else '',
                'subcategory': fields[5] if len(fields[5]) > 0 else '',
                'zip': fields[6] if len(fields[6]) > 0 else '',
                'address': fields[7] if len(fields[7]) > 0 else '',
                'prefecture': fields[8] if len(fields[8]) > 0 else '',
                'area': fields[9] if len(fields[9]) > 0 else '',
                'city': fields[10] if len(fields[10]) > 0 else '',
                'latitude': latitude,
                'longitude': longitude,
                'photos': fields[13] if len(fields[13]) > 0 else '',
                'videos': fields[14] if len(fields[14]) > 0 else '',
                'webcontents': fields[15] if len(fields[15]) > 0 else '',
                'status': fields[16] if len(fields[16]) > 0 else '',
                'portal': fields[17] if len(fields[17]) > 0 else '',
                'group': fields[18] if len(fields[18]) > 0 else '',
                'phone': fields[19] if len(fields[19]) > 0 else '',
                'fax': fields[20] if len(fields[20]) > 0 else '',
                'email': fields[21] if len(fields[21]) > 0 else '',
                'facility': fields[22] if len(fields[22]) > 0 else '',
                'remark': fields[23] if len(fields[23]) > 0 else '',
                'tags': fields[24] if len(fields[24]) > 0 else '',
                'hidden_location': fields[25] if len(fields[25]) > 0 else False,
                'auto_checkin': fields[26] if len(fields[26]) > 0 else False,
                'checkin_radius': fields[27] if len(fields[27]) > 0 else 15,
                'checkin_point': fields[28] if len(fields[28]) > 0 else 10,
                'buy_point': fields[29] if len(fields[29]) > 0 else 0,
                'evaluation_value': fields[30] if len(fields[30]) > 0 else '',
                'shop_closed': fields[31] if len(fields[31]) > 0 else False,
                'shop_shutdown': fields[32] if len(fields[32]) > 0 else False,
                'opening_hours_mon': fields[33] if len(fields[33]) > 0 else '',
                'opening_hours_tue': fields[34] if len(fields[34]) > 0 else '',
                'opening_hours_wed': fields[35] if len(fields[35]) > 0 else '',
                'opening_hours_thu': fields[36] if len(fields[36]) > 0 else '',
                'opening_hours_fri': fields[37] if len(fields[37]) > 0 else '',
                'opening_hours_sat': fields[38] if len(fields[38]) > 0 else '',
                'opening_hours_sun': fields[39] if len(fields[39]) > 0 else ''
            }
            if geom:
                defaults['geom'] = geom

            obj, created = mdl.objects.update_or_create(
                location_id=int(fields[0]),
                defaults=defaults
            )
            
            if created:
                logger.info(f"New location created with id: {obj.location_id}")
            else:
                logger.info(f"Location updated with id: {obj.location_id}")

    except Exception as e:
        logger.error(f"Error updating or creating location: {e}", exc_info=True)


def updateLocation(mdl, fields):
    print(f"Updating {fields[0]} - {fields[1]}")
    print(mdl.objects.filter(location_id = int(fields[0])))
    print(f"--- ${fields} ----")
   
    try:
        with transaction.atomic():
            updated = mdl.objects.filter(location_id = int(fields[0])).update(
                sub_loc_id = fields[1] if len(fields[1]) > 0 else '',
                cp = fields[2] if len(fields[2]) > 0 else 0,
                location_name = fields[3] if len(fields[3]) > 0 else '',
                category = fields[4] if len(fields[4]) > 0 else '',
                subcategory = fields[5] if len(fields[5]) > 0 else '',
                zip = fields[6] if len(fields[6]) > 0 else '',
                address = fields[7] if len(fields[7]) > 0 else '',
                prefecture = fields[8] if len(fields[8]) > 0 else '',
                area = fields[9] if len(fields[9]) > 0 else '',
                city = fields[10] if len(fields[10]) > 0 else '',
                latitude = fields[11] if len(fields[11]) > 0 else '',
                longitude = fields[12] if len(fields[12]) > 0 else '',
                photos = fields[13] if len(fields[13]) > 0 else '',
                videos = fields[14] if len(fields[14]) > 0 else '',
                webcontents = fields[15] if len(fields[15]) > 0 else '',
                status = fields[16] if len(fields[16]) > 0 else '',
                portal = fields[17] if len(fields[17]) > 0 else '',
                group = fields[18] if len(fields[18]) > 0 else '',
                phone = fields[19] if len(fields[19]) > 0 else '',
                fax = fields[20] if len(fields[20]) > 0 else '',
                email = fields[21] if len(fields[21]) > 0 else '',
                facility = fields[22] if len(fields[22]) > 0 else '',
                remark = fields[23] if len(fields[23]) > 0 else '',
                tags = fields[24] if len(fields[24]) > 0 else '',
                hidden_location = fields[25] if len(fields[25]) > 0 else False,
                auto_checkin = fields[26] if len(fields[26]) > 0 else False,
                checkin_radius = fields[27] if len(fields[27]) > 0 else 15,
                checkin_point = fields[28] if len(fields[28]) > 0 else 10,
                buy_point = fields[29] if len(fields[29]) > 0 else 0,
                evaluation_value = fields[30] if len(fields[30]) > 0 else '',
                shop_closed = fields[31] if len(fields[31]) > 0 else False,
                shop_shutdown = fields[32] if len(fields[32]) > 0 else False,
                opening_hours_mon = fields[33] if len(fields[33]) > 0 else '',
                opening_hours_tue = fields[34] if len(fields[34]) > 0 else '',
                opening_hours_wed = fields[35] if len(fields[35]) > 0 else '',
                opening_hours_thu = fields[36] if len(fields[36]) > 0 else '',
                opening_hours_fri = fields[37] if len(fields[37]) > 0 else '',
                opening_hours_sat = fields[38] if len(fields[38]) > 0 else '',
                opening_hours_sun = fields[39] if len(fields[39]) > 0 else ''
            )
            logger.info(f"TempLocation updated successfully. Rows affected: {updated}")

    except Exception as e:
        logger.error(f"Error updating TempLocation: {e}", exc_info=True)

        
def updateLineTable(mdl, fields):
    print(f"Updating {fields[0]} - {fields[1]}")
    print(mdl.objects.filter(location_id = int(fields[0])))
    print("-------")
    with transaction.atomic():
        mdl.objects.filter(location_id = int(fields[0])).update(
        location_name= fields[1] if len(fields) > 1 else '',
        category=fields[2] if len(fields) > 2 else '',
        zip=fields[3] if len(fields) > 3 else '',
        address=fields[4] if len(fields) > 4 else '',
        prefecture=fields[5] if len(fields) > 5 else '',
        area=fields[6] if len(fields) > 6 else '',
        city=fields[7] if len(fields) > 7 else '',
        photos=fields[8] if len(fields) > 8 else '',
        videos=fields[9] if len(fields) > 9 else '',
        webcontents=fields[10] if len(fields) > 10 else '',
        status=fields[11] if len(fields) > 11 else '',
        portal=fields[12] if len(fields) > 12 else '',
        group=fields[13] if len(fields) > 13 else '',
        phone=fields[14] if len(fields) > 14 else '',
        fax=fields[15] if len(fields) > 15 else '',
        email=fields[16] if len(fields) > 16 else '',
        facility=fields[17] if len(fields) > 17 else '',
        remark=fields[18] if len(fields) > 18 else '',
        tags=fields[19] if len(fields) > 19 else '',
        parammeters=fields[20] if len(fields) > 20 else ''
        )


def updatePolygonTable(mdl, fields):
    #print(f"Updated {fields[0]} - {fields[1]}")
    with transaction.atomic():
        mdl.objects.filter(location_id = fields[0]).update(
        location_name= fields[1] if len(fields) > 1 else '',
        category=fields[2] if len(fields) > 2 else '',
        zip=fields[3] if len(fields) > 3 else '',
        address=fields[4] if len(fields) > 4 else '',
        prefecture=fields[5] if len(fields) > 5 else '',
        area=fields[6] if len(fields) > 6 else '',
        city=fields[7] if len(fields) > 7 else '',
        photos=fields[8] if len(fields) > 8 else '',
        videos=fields[9] if len(fields) > 9 else '',
        webcontents=fields[10] if len(fields) > 10 else '',
        status=fields[11] if len(fields) > 11 else '',
        portal=fields[12] if len(fields) > 12 else '',
        group=fields[13] if len(fields) > 13 else '',
        phone=fields[14] if len(fields) > 14 else '',
        fax=fields[15] if len(fields) > 15 else '',
        email=fields[16] if len(fields) > 16 else '',
        facility=fields[17] if len(fields) > 17 else '',
        remark=fields[18] if len(fields) > 18 else '',
        tags=fields[19] if len(fields) > 19 else '',
        parammeters=fields[20] if len(fields) > 20 else ''
        )


def createUser(fields):
    with transaction.atomic():
        other_fields.setdefault('event_code',fields[1])
        other_fields.setdefault('team_name',fields[1])
        other_fields.setdefault('group',fields[1])
        user = CustomUser.objects.create_user(email=fields[0], password=fields[4], **other_fields)
        user.is_superuser = False
        user.is_staff = False
        user.save()


@receiver(post_delete,sender=UserUpload)
def deleteUserUploadUser(sender,instance,*args,**kwargs):
    pass
    emails = UserUploadUser.objects.filter(userfile=instance.name).values_list('email')
    print("------- email----")
    print(emails)
    print("------- name----")
    CustomUser.objects.filter(email__in=emails).delete()
    templocation.objects.all().delete()
    UserUploadUser.objects.filter(userfile=instance.name).delete();


@receiver(post_save, sender=UserUpload)
def publish_data(sender, instance, created, **kwargs):
    logger.info(f"Processing ShapeLayer: {instance.name}")

    file = instance.file.path
    #os.remove(file)

    try:
        #csv_f = glob.glob(r'{}/**/*.csv'.format(file), recursive=True)[0]

        remove_bom_inplace(file)
        print(file)
        with open(file, mode="r", encoding="utf-8") as txt_file:
            #heading = next(txt_file)
            reader = csv.reader(txt_file, delimiter=",")
            for fields in reader:
                print("@@@@@@@@@@@@")
                # print(fields[0])
                # print(fields[1])
                # print(fields[2])
                print("@@@@@@@@@@@@")
                createUser(fields)

        with open(file, mode="r", encoding="utf-8") as txt_file:
            reader_2 = csv.reader(txt_file, delimiter=",")
            for fields in reader_2:
                    insertUserUploadUser(instance.name, fields)
    except Exception as e:
         print('######## user csv file ##########',e)


# for upper compatible

# 既存のモデルに追加=> 通過記録に相応しい名称に変更すべき

def get_default_entry():
    """
    デフォルトのEntryを取得または作成する
    """
    try:
        # NewEvent2のデフォルトイベントを取得
        default_event = NewEvent2.objects.first()
        if default_event:
            # デフォルトチームを取得
            default_team = Team.objects.first()
            if default_team:
                # 既存のEntryを取得
                entry = Entry.objects.filter(
                    teams=default_team,
                    event=default_event
                ).first()
                if entry:
                    return entry.id
                # 新しいEntryを作成
                from django.contrib.auth import get_user_model
                User = get_user_model()
                default_user = User.objects.first()
                if default_user:
                    entry = Entry.objects.create(
                        event=default_event,
                        main_user=default_user
                    )
                    entry.teams.add(default_team)
                    return entry.id
    except:
        pass
    return None


class GpsLog(models.Model):
    """
    GPSチェックイン情報を管理するモデル
    gps_informationテーブルに対応
    """
    serial_number = models.IntegerField(null=False)
    
    # Entry へ移行
    zekken_number = models.TextField(null=False, default='')
    event_code = models.TextField(null=False, default='')

    cp_number = models.TextField(null=True, blank=True)
    image_address = models.TextField(null=True, blank=True)

    # 新規追加
    checkin_time = models.DateTimeField(auto_now_add=True)

    goal_time = models.TextField(null=True, blank=True)
    late_point = models.IntegerField(null=True, blank=True)
    create_at = models.DateTimeField(null=True, blank=True)
    create_user = models.TextField(null=True, blank=True)
    update_at = models.DateTimeField(null=True, blank=True)
    update_user = models.TextField(null=True, blank=True)
    buy_flag = models.BooleanField(null=True, blank=True)
    minus_photo_flag = models.BooleanField(null=True, blank=True)
    colabo_company_memo = models.TextField(null=False, default='')

    # 新規追加
    is_service_checked = models.BooleanField(default=False)

    # ゴール記録用に追加
    score = models.IntegerField(default=0, null=True, blank=True)
    scoreboard_url = models.URLField(blank=True, null=True)

    class Meta:
        db_table = 'gps_information'
        # 複合主キーの設定
        unique_together = [['serial_number', 'zekken_number', 'event_code', 'colabo_company_memo']]
        # インデックスの設定（必要に応じて）
        indexes = [
            models.Index(fields=['zekken_number', 'event_code'], name='gpslog_zekken_event_idx'),
            models.Index(fields=['create_at'], name='gpslog_create_at_idx'),
        ]

    def __str__(self):
        return f"{self.event_code}-{self.zekken_number}-{self.serial_number}"

    
    @classmethod
    def record_start(cls, entry):
        """
        チームのスタート情報を記録する
        以前はTeamStartモデルが担当していた機能
        """
        return cls.objects.create(
            serial_number=0,  # スタートログを表す特別な値
            entry=entry,
            zekken_number=entry.zekken_number,
            event_code=entry.event.event_name,
            cp_number="START",
            create_at=timezone.now(),
            update_at=timezone.now(),
            buy_flag=False,
            colabo_company_memo=""
        )

    @classmethod
    def record_goal(cls, entry, goal_time=None, image_url=None, score=0, scoreboard_url=None):
        """
        チームのゴール情報を記録する
        以前はTeamGoalモデルが担当していた機能
        """
        if goal_time is None:
            goal_time = timezone.now()
            
        return cls.objects.create(
            serial_number=9999,  # ゴールログを表す特別な値
            entry=entry,
            zekken_number=entry.zekken_number,
            event_code=entry.event.event_name,
            cp_number="GOAL",
            image_address=image_url,
            create_at=goal_time,
            update_at=timezone.now(),
            goal_time=goal_time.strftime('%Y-%m-%d %H:%M:%S'),
            buy_flag=False,
            score=score,
            scoreboard_url=scoreboard_url,
            colabo_company_memo=""
        )
        
    def is_start_record(self):
        """このレコードがスタート記録かどうかを判定"""
        return self.cp_number == "START" and self.serial_number == 0
    
    def is_goal_record(self):
        """このレコードがゴール記録かどうかを判定"""
        return self.cp_number == "GOAL" and self.serial_number == 9999
  

    @property
    def start_time(self):
        """スタート時刻を返す（TeamStartとの互換性のため）"""
        return self.create_at or self.checkin_time

    @property
    def goal_datetime(self):
        """ゴール時刻をDateTimeとして返す（TeamGoalとの互換性のため）"""
        if self.is_goal_record() and self.create_at:
            return self.create_at
        return None



class Waypoint(models.Model):
    entry = models.ForeignKey('Entry', on_delete=models.CASCADE, related_name='waypoints')
    latitude = models.FloatField()
    longitude = models.FloatField()
    altitude = models.FloatField(null=True, blank=True)
    accuracy = models.FloatField(null=True, blank=True)
    speed = models.FloatField(null=True, blank=True)
    recorded_at = models.DateTimeField()
    created_at = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        ordering = ['recorded_at']
        indexes = [
            models.Index(fields=['entry', 'recorded_at']),
        ]
    
    def __str__(self):
        return f"{self.entry.team_name} - {self.recorded_at.strftime('%Y-%m-%d %H:%M:%S')}"